checkbox未赋值时获取value是on_【漏洞分析】关于mysqlconnectorjava连接时的反序列化...

01

漏洞概述

该漏洞是Black Hat Europe 2019的议题《New Exploit Technique In Java Deserialization Attack》。

只要用户能控制客户端的JDBC连接串,在连接阶段即可触发该漏洞,无需继续执行SQL语句。

mysql-connector-java是Java进行JDBC执行的依赖包,支持在Java代码中对数据库进行连接与SQL语句执行等。

02

影响范围

mysql-connector-java-5.1.18 ~ 5.1.40

mysql-connector-java-6.x

mysql-connector-java-8.0.7以上

03

修复建议

建议在项目开发时,不允许MySQL连接串被用户控制。

如果存在功能需求(如DolphinScheduler),建议将mysql-connector-java-5版本升级到5.1.40以上,6、8版本升级到最新版本。

04

漏洞分析

该漏洞在mysql-connector-java-5、6、8的某些版本中都存在,只不过利用链不同,下面首先分析mysql-connect-java-5版本。

 mysql-connect-java-5版本 

注:本次分析利用的环境为mysql-connector-java-5.1.29 。

mysql-connect-java-5版本中利用的是detectCustomCollations的触发方式,触发点在com.mysql.jdbc.ConnectionImpl的buildCollationMapping方法中。

b86529d09dd2ffffc5f9127f1626a626.png

可以看到,只要MySQL版本大于4,且getDetectCustomCollations( )为true即可。之后会再次判断服务器的MySQL版本是否在5以上,接着调用Util.resultSetToMap( ) 。

c80a0f1046a46f359e36a5b9689f245c.png

然后会调用传入参数result的getObject( )方法,跟进getObject( )方法。

注:此处直接Ctrl+鼠标左键不能直接进入调用getObject( )方法的地方,只会跳到interface类的方法中,真正的getObject( )方法在该interface类的impl类中。

补充说明:传入的result对象是stmt.executeQuery("SHOW COLLATION")的返回值,stmt对象是通过getMetadataSafeStatement( )获取的,跟进该方法,可以看到声明stmt的方法为createStatement( ) 。在这个方法中可以看到,该链实际返回的stmt对象是StatementImpl类创建出的对象,因此原来的stmt.executeQuery("SHOW COLLATION")中执行的executeQuery( )方法,是在StatementImpl类中重写的executeQuery( )方法,即传入的result对象是executeQuery( )方法执行后的返回值。

而在executeQuery("SHOW COLLATION")方法中,返回的result对象是通过this.results = locallyScopedConn.execSQL( )赋值的,locallyScopedConn对象是this.connetion赋值的,所以需要找到this.connetion的赋值处。存在于StatementImpl的构造方法中,传入的是MySqlConnection的对象。根据刚刚创建stmt的流程,传入的是getLoadBalanceSafeProxy( )的返回结果。根据结果,最后定位在ConnectionImple类的execSQL( )方法,返回的是ResultSetInternalMethods类的对象,但ResultSetInternalMethods是一个接口类,所以最终的实例化对象必然是其实现类,而ResultSetImpl恰好是该类的实现类,所以继续向下跟踪,必然会找到返回该类的实例化对象或是其子类的实例化对象的语句。最后,执行完成后返回的对象是JDBC4ResultSet类的对象,且该类继承了ResultSetImpl类。

综上所述,在Util.resultSetToMap( )中调用的getObject( )方法如果在JDBC4ResultSet类中未被重写的话,会调用父类的getObject( )方法。经过审计代码,发现JDBC4ResultSet类只重写了如下的重载方法,所以此处应该调用父类的getObject( )方法。

public T getObject ( int columnIndex, Class type ) throws SQLException

在跟进该方法时,代码如下:

51af477711da8d34bcc5efa590d056b7.png

可以看出,此处获取了查询结果,且对于结果集的每一列进行了getSQLType判断。当此处的返回值为-2时,会判断该字段是否为二进制串,且是否为BLOB类型,接着会判断是否开启了反序列化,以及是否为Java反序列化开头的二进制数据,最终可以实现readObject( )方法进行反序列化。

也就是说,只要字段2或字段3搭载了序列化数据,在其处理过程中就会进行反序列化,从而执行恶意代码。

fdb0477bb1e4f89eeab7293f7e32d9ac.png

POC构造

想要复现该漏洞,需要准备一个带有恶意对象的数据库表,由于要在SHOW COLLATION执行处触发,所以带有恶意对象的表应该在系统表里(即INFORMATION_SCHEMA COLLATIONS表),当mysql_jdbc_connect对恶意数据库进行连接请求时会触发反序列化。

但是修改系统表可能会引入其他未知问题。针对这种情况,可以采用其他方法。

这里要提到mysql_jdbc_connect的工作原理。mysql_jdbc_connect是通过与数据库端口进行私有协议(即MySQL协议)来交互的,那么可以伪造一个MySQL服务端,让mysql_jdbc_connect连接虚假服务端,将恶意序列化对象插入返回数据中,由mysql_jdbc_connect进行反序列化执行即可。

想要构造这个数据包,需要了解MySQL私有协议的交互流程。这里抓取正常的本地loop报文,简单分析交互流程。

握手报文如下:

106495d4ed54c3debc193942d27062bd.png

连接请求如下:

2366e323179640b5373df8acb2f18fa8.png

认证响应如下:

f7df4374b4662c0ec3cd8bbd77bba5e1.png

流程大致如上图,后续每执行一次SQL相关操作,就会通过该协议与数据库进行一次交互。

上文提到,触发点在连接时数据库执行SHOW COLLATION的地方,所以重点关注SHOW COLLATION的请求与响应。

SHOW COLLATION请求如下:

cefe171c06c1f3388374e148e341a297.png

SHOW COLLATION响应如下:

252038e23f0c3b166ead948993ec5ad1.png

使用python构造一个恶意的MySQL服务端,用于监听客户端的连接(握手报文构造与上面一致即可),SHOW COLLATION响应代码如下:

632e25d8b48168d7d92529f6f81a136f.png

0711e6226ec6a44971ac5e1ecd492e78.png

注:1、由于connector 5版本的getObject( )方法需要用到第三列数据,所以构造响应时需要至少构造三列响应数据;

2、需要FLAGS大于128、来源表不为空,否则会被当成Text 。

复现结果

启动模拟MySQL监听后,使用Java作为客户端发出连接请求。

ee0061820cc90d5ce6eb12bb0f84aa58.png

执行getConnection( )方法时计算器弹出。

 mysql-connect-java-8版本 

注:本次分析利用的环境为mysql-connector-java-8.0.14 。

8版本connector的触发点与5版本的触发点不同。

前文提到,5版本的触发点是detectCustomCollations为true,且触发位置在com.mysql.jdbc.ConnectionImpl的buildCollationMapping方法中。

而8版本则使用ServerStatusDiffInterceptor的触发方式,这也是原议题中给出的环境与触发方式。触发点位于com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor类的populateMapWithSessionStatusValues方法中。

6c850731e2cae8231c828a2e9767045c.png

进入该方法,会调用到ResultSetUtil类的resultSetToMap方法。

56156d4e96bd134069f86d7d4a22bb42.png

再进入该方法,会调用ResultSet的派生类重写的getObject( )方法,此处的ResultSet对象是createStatement( )创建的对象执行executeQuery("SHOW SESSION STATUS")后返回的结果,通过跟踪,最终定位到真正调用getObject( )方法的是com.mysql.cj.jdbc.result.ResultSetImpl类的getObject( )方法。

之后和5版本的判断基本相同,判断是否为二进制,判断是否为BLOB类型的数据,最后再判断自动反序列化是否开启。

4803e60c4d85b65344e5662c21877117.png

从ServerStatusDiffInterceptor到反序列化这条链已经弄通,接下来分析如何在JDBC连接的时候,调用ServerStatusDiffInterceptor类的populateMapWithSessionStatusValues方法。

首先看到ServerStatusDiffInterceptor类,它是一个拦截器,并且实现了QueryInterceptor接口。

补充说明:Connector/J拦截器类,仅在8.0.7版本以上使用。

拦截器是一种软件设计模式,提供了一种透明的方式来扩展或修改程序的某些方面,类似于用户出口,无需重新编译。使用Connector/J,通过更新连接字符串以引用实例化的不同组的拦截器类,可以启用和禁用拦截器。

可以在queryInterceptors中指定实现com.mysql.cj.interceptors.QueryInterceptor接口的类的标准名称。在这种拦截器类中,可能会更改或增强某些类型的语句所完成的处理,例如自动检查内存缓存服务器中的查询数据,重写慢速查询,记录有关语句的执行信息或请求路由到远程服务器。

在JDBC的连接串中可以控制拦截器的连接属性,如:

jdbc:mysql://your-ip:3306/xxx?queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

当拦截器在JDBC连接串中使用时,其作用是在执行SQL语句查询前后,通过一些特定操作来影响查询结果。所以要触发queryInterceptors则需要能执行SQL查询语句,而在getConnection过程中,会触发SET NAMES utf、set autocommit=1一类的请求,因此会触发配置的queryInterceptors 。

从代码层面也可以看出这套逻辑:

1、在初始化过程中,会调用ConnectImpl类的setAutoCommit( )方法,该方法会执行SET autocommit语句。

b66fde968b6419c8c15083c4d235f561.png

2、执行execSQL( )方法会调用到sendQueryString( ),之后会调用sendQueryPacket( )方法。

d43598ee970ec0e69a19eff95b2fa8f4.png

3、在sendQueryPacket( )方法中会判断是否设置了queryInterceptors,如果设置了拦截器,则通过反射调用其preProcess( )方法。

b65545e2ac2c62f11fa6a03025060f99.png

4、最后从ServerStatusDiffInterceptor的preProcess( )方法中调用populateMapWithSessionStatusValues( )方法,进入利用链。

7ff6d1c2b96bffdaf5ebc7af09f8ee4f.png

POC构造

与5版本类似,只有两个地方不同:

1、JDBC连接串的URL

在5版本中需要设置detectCustomCollations为true;而在8版本中,需要设置queryInterceptors,连接串如下:

jdbc:mysql://127.0.0.1:3306/vaethink?queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

2、恶意响应的构造

在5版本中,根据查询语句“SHOW COLLATION”的Query包进行恶意响应构造;而在8版本中,需要对“SHOW SESSION STATUS”的Query包进行恶意响应构造。

模拟的MySQL服务端只需要增加对show session status包的响应即可。下附网上的POC代码,其中get_payload_content方法中获取的是可弹出计算器的序列化对象。

c8161bddb243200d33fc3f243dcc1114.png

复现结果

启动模拟MySQL监听后,使用Java作为客户端发出连接请求。

28f9d0558ee5c002f0b504dcdd77a395.png

计算器弹出,使用的是该POC的默认序列化对象,即弹出计算器的序列化对象。

ccd95d0f6e5437e8e807d62ed4bd1443.png

05

参考链接

https://xz.aliyun.com/t/8159

https://blog.csdn.net/fnmsd/article/details/106232092

   安博通 ,可视化网络安全技术创新者    威胁情报 【漏洞预警】Apache Shiro绕过权限漏洞(CVE-2020-17510) 【漏洞预警】ZenTaoPMS 文件上传漏洞(CNVD-C-2020-121325) 【漏洞预警】Apache Kylin未授权配置泄露漏洞(CVE-2020-13937) e32badff71df09b6d7e3ae12ececc5de.gif

点击,了解安博通

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值