Jdbc反序列化[绕过|修复|分析]

admin 2023年9月12日16:34:48评论21 views字数 2856阅读9分31秒阅读模式

前言

此文章主要是对mysql Jdbc驱动连接过程的分析,本次分析使用的mysql-connector-java版本为8.0.14。

0x01 jdbc标准流程

oracle约束的jdbc建立连接的标准流程其他的数据库只需要按照这套标准实现自己的连接驱动即可,我们只关注其建立连接的步骤,之后的数据交互不做分析。oracleJDBC官方文档

mysql connector/j开发指南

总结后,jdbc的连接流程如下:

  1. 注册数据库连接驱动,比如:Class.forName("com.mysql.cj.jdbc.Driver")
  2. 连接数据库,比如:Connection con= DriverManager.getConnection(c,properties);

0x02 mysql jdbc连接流程

我们直接来到mysql的连接流程,驱动会调用mysql驱动com/mysql/cj/jdbc/NonRegisteringDriver.java的connect()函数。connect()函数的具体处理流程如下:

  1. 判断是否能处理连接字符串,使用正则提取:之前的部分比如jdbc:mysql:作为scheme。

  2. 对连接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

  3. 接着,会初始化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的类都可以作为查询拦截器。

  4. 至此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 修复方案

  1. 对于jdbc反序列化来说,可以通过将mysql-connector/j升级到8.0.21来解决该问题,在mysql-connector/j8.0.21的ServerStatusDiffInterceptor#populateMapWithSessionStatusValues中不再使用getObject而是使用getString。

Jdbc反序列化[绕过|修复|分析]Jdbc反序列化[绕过|修复|分析]Jdbc反序列化[绕过|修复|分析]

  1. 同时也可以在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反序列化[绕过|修复|分析]


原文始发于微信公众号(地表最强伍迪哥):Jdbc反序列化[绕过|修复|分析]

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月12日16:34:48
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Jdbc反序列化[绕过|修复|分析]http://cn-sec.com/archives/2029318.html

发表评论

匿名网友 填写信息