JDBC反序列化
Mysql
环境设置
$5.1.11\leq$
mysql-connector-java-5.1.26commons-collections-3.2(用来打反序列化链)
payload
fake_mysql_serverpayload
漏洞分析
- 调用堆栈
DriverManager#getConnection跟进之后,在ConnectionImpl#connectOneTryOnly中设置拦截器(拦截器的作用是在查询语句执行前后,拦截影响最终的执行结果)。
接着跟进
com.mysql.jdbc.ConnectionImpl#loadServerVariables最后会到执行函数executeQuery来执行拼接的sql语句。
- 接着一直调用到
com.mysql.jdbc.MysqlIO#sqlQueryDirect方法时,判断statementInterceptors是否为空,不为空则调用之前url中写入的ServerStatusDiffInterceptor,这里的判断很有意思,写了一堆,最后可以只由一个条件this.statementExecutionDepth == 1决定,这个条件由之前执行过一次的sqlQueryDirect自加,初始化的时候是0,满足条件。

- 接着一直调用到
- 接着调用到查询
SHOW SESSION STATUS的地方,通过这个查询获取恶意类
想要进行readObject还需要去设置前面url中的参数
autoDeserialize=true
然后很奇怪的是,用网上使用的mysql服务器不知道为什么需要加多一行才能出发反序列化的过程
分析数据包
- 接着调用到查询
这里是调用sink:
getObject的函数,而第一次getObject,column=1的时候,不会调用readObject,所以关注点放在第二次get
getObject(2)的时候,如果传入的filed少于两个,代码中检验了要反序列化的filed是否溢出,就会出现问题,所以至少需要传入两个,以防止被抛出异常
最后网上的数据包没有加上EOF标志位,识别不出来数据为
row packet,加上就可以
com.mysql.jdbc.BufferRow#getColumnValue从row packet获取数据:
至于为什么加多一行也可以,可能是设置的column count为2,而后续我放了3个field packet,导致虽然在wireshark里面识别成field packet,但在代码里读取的时候当成row packet(我猜的)
Ref
- https://xz.aliyun.com/t/8159
- https://pyn3rd.github.io/2022/06/06/Make-JDBC-Attacks-Brillian-Again-I/
其他版本区别
8.x < 8.0.20
- 环境搭建
pom.xml:
1 | <dependency> |
- 漏洞分析
区别在于:
调用com.mysql.cj.jdbc.ConnectionImpl#connectOneTryOnly时,使用的是queryInterceptor
向服务器发送一次查询请求:
跟进到拦截器进行执行前操作:
然后利用查询SHOW SESSION STATUS的结果,执行到调用sink点的位置:
数据包显示,去掉了EOF标志也能正常识别,可能是改了协议:
6.x
- 环境搭建
1 | <dependency> |
- 漏洞分析
Interceptors选择的是statementInterceptors,剩下的调用和$5.1.11\leq$没有区别
5.1.10 >=
区别:
初始化在调用
createNewIO之后,调用时执行的Interceptors中没有设置,所以getConnection之后需要再次执行查询生效:
8.0.20 <
官方修复:不读序列化数据了,改成读字符串