前言
0x01 jdbc标准流程
mysql connector/j开发指南
总结后,jdbc的连接流程如下:
-
注册数据库连接驱动,比如: Class.forName("com.mysql.cj.jdbc.Driver")
-
连接数据库,比如: Connection con= DriverManager.getConnection(c,properties);
0x02 mysql jdbc连接流程
我们直接来到mysql的连接流程,驱动会调用mysql驱动com/mysql/cj/jdbc/NonRegisteringDriver.java的connect()函数。connect()函数的具体处理流程如下:
-
判断是否能处理连接字符串,使用正则提取:之前的部分比如jdbc:mysql:作为scheme。
-
对连接url进行处理,将其转换为ConnectionUrl对象。转换的过程包括
-
通过正则匹配将连接url分为scheme、authority、path、query、fragment正则代码在ConnectionUrlParser.java的CONNECTION_STRING_PTRN中。由于fragment是从#开始匹配,所以在jdbc反序列化中我们可以直接将连接字符串跟在数据库名后面再通过#号将后面原有的query字段改为fragment部分,达到截断链接字符串的目的。
-
如果设置了useConfigs参数,则可以设置连接配置文件。从中读取配置信息。详见
com/mysql/cj/conf/ConnectionUrl.java#expandPropertiesFromConfigFiles
-
将连接url的参数转换为properties,在转换时会将参数的key和value都urldecode解码一下,所以该点可能存在连接字符绕过的风险。详见
com/mysql/cj/conf/ConnectionUrlParser.java#processKeyValuePattern
-
将getConnection时携带的Properties参数加入url属性当中,如果在url中定义了该属性则覆盖。在getConnection时传入的user和password就输入Properties参数,用户可以在Properties中加入更多的url参数。
-
在创建HostInfo时参数设置有protocol=PIPE和path参数时就可以在path位置创建PIPE文件。详见
com/mysql/cj/conf/ConnectionUrl.java#fixProtocolDependencies
-
接着,会初始化ConnectionImpl类(默认jdbc:mysql://)在初始化过程中驱动会根据连接url中携带的参数来设置缓存区、代理和查询拦截器。
-
查询拦截器就是引起jdbc反序列化的地方,在url连接参数中可以通过设置QueryInterceptors参数来指定SQL语句查询结果的处理过程。所以我们设置为
com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor
时,当我们获取查询结果时,处理流程就会来到ServerStatusDiffInterceptor的postProcess函数中,接着触发postProcess->populateMapWithSessionStatusValues->ResultSetUtil.resultSetToMap->ResultSetImpl.getObject。最后在数据类型为BLOB以及url参数autoDeserialize=True的情况下触发objIn.readObject()。 -
只要实现了QueryInterceptor的类都可以作为查询拦截器。
-
至此mysql JDBC就已经完成了连接。
0x03 JDBC反序列化关键字绕过
通过0x02里我们对连接流程的分析我们可以总结出以下几个值得关注的点:
-
mysql-connector/j 8.x的连接过程中会将#后面的部分认为是fragment所以我们可以通过#来阶段url连接字符,所以当遇到对数据库名中的autoDeserialize和queryInterceptors做过滤时,我们可以尝试在数据库ip或者端口参数后面加入 /test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor#
来完成关键字的绕过。关于这一点是否在5.x、6.x有效,笔者没有分析,不过大可一试! -
mysql-connector/j 8.x的连接过程中会对参数名和数值进行url解码,所以在遇到对所有参数中的autoDeserialize和queryInterceptors做过滤时我们可以尝试单次/多次url编码关键字来绕过,例如: %61utoDeserialize、%2561utoDeserialize
。需要注意的是笔者在5.x版本的连接过程中发现并不会进行url解码。
0x04 修复方案
-
对于jdbc反序列化来说,可以通过将mysql-connector/j升级到8.0.21来解决该问题,在mysql-connector/j8.0.21的 ServerStatusDiffInterceptor#populateMapWithSessionStatusValues
中不再使用getObject而是使用getString。
-
同时也可以在getConnection时提前设置好properties中autoDeserialize属性为false。这样即使攻击者在连接url中设置了autoDeserialize参数也会被覆盖掉。
String c="jdbc:mysql://127.0.0.1:3306/test";
Properties properties=new Properties();
properties.setProperty("autoDeserialize","false");
properties.setProperty("user","1");
properties.setProperty("passwd","2");
Connection con= DriverManager.getConnection(c,properties);
创作不易,转载需注明出自公众号"地表最强伍迪哥"
原文始发于微信公众号(地表最强伍迪哥):Jdbc反序列化[绕过|修复|分析]
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论