• JDBC导致的反序列化攻击


    背景

    上周BlackHat Europe 2019的议题《New Exploit Technique In Java Deserialization Attack》中提到了一个通过注入JDBC URL实现反序列化攻击的场景,简单分析一下。

    分析

    当java应用使用MySQL Connector/J(官方的JDBC驱动,本文基于其8.0+版本)连接mysql时会导致潜在的反序列化攻击风险,JDBC URL的格式如下:protocol//[hosts]/[database]?properties,具体可看mysql官方文档,示例:jdbc:mysql://localhost:3306/test?useSSL=true

    其中,protocol、host、database都比较好理解,URL中的properties可以设定MySQL Connector/J连接mysql服务器的具体方式,关于properties的官方文档地址,其中和本文相关的连接属性有两个,分别是autoDeserializequeryInterceptors,前者是设定MySQL Connector/J是否反序列化BLOB类型的数据,后者是拦截器,在查询执行时触发,由com.mysql.cj.protocol.a.NativeProtocol#sendQueryPacket方法源码可知,会在执行查询语句前后分别调用拦截器的preProcess和postProcess方法。

    接下来定位下反序列化的触发点,在mysql-connector-java组件下全局搜索关键字“.readObject()”,定位到com.mysql.cj.jdbc.result.ResultSetImpl类中的getObject(int columnIndex)方法,部分核心代码如下:

    public Object getObject(int columnIndex) throws SQLException {
    ……
    case BLOB:
      byte[] data = getBytes(columnIndex);
      if (this.connection.getPropertySet().getBooleanProperty(PropertyDefinitions.PNAME_autoDeserialize).getValue()) {
         Object obj = data;
         // Serialized object?
         try {
           ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
           ObjectInputStream objIn = new ObjectInputStream(bytesIn);
           obj = objIn.readObject();
         }
      }
    }
    

    变量data即为mysql返回结果集,当JDBC URL中设定属性autoDeserialize为true时,会对类型为bit、binary以及blob的数据进行反序列化,如何触发getObject(int columnIndex)方法的调用呢?议题中给出的调用链如下:

    > com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor#preProcess/postProcess
    > com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor#populateMapWithSessionStatusValues
    > com.mysql.cj.jdbc.util.ResultSetUtil#resultSetToMap
    > com.mysql.cj.jdbc.result.ResultSetImpl#getObject
    
    
    

    ServerStatusDiffInterceptor即为此前提到过的拦截器,在JDBC URL中设定属性queryInterceptors为ServerStatusDiffInterceptor时,执行查询语句会调用拦截器的preProcess和postProcess方法,进而通过上述调用链最终调用getObject(int columnIndex)方法。

    实际利用还有一个问题,最终调用getObject方法的对象是数据库返回的结果集,由populateMapWithSessionStatusValues方法可知:

    try {
        toPopulate.clear();
    
        stmt = this.connection.createStatement();
        rs = stmt.executeQuery("SHOW SESSION STATUS");
        ResultSetUtil.resultSetToMap(toPopulate, rs);
    }
    

    这个结果集是执行SQL语句“SHOW SESSION STATUS”后数据库返回的值,SQL语句“SHOW SESSION STATUS”返回当前数据库连接的状态值,实际是读取系统表INFORMATION_SCHEMA.SESSION_VARIABLES的值,也可能是PERFORMANCE_SCHEMA.SESSION_VARIABLES(Mysql版本差异导致)。但是mysql中INFORMATION_SCHEMA和PERFORMANCE_SCHEMA都是不允许被修改的,所以需要想办法操纵返回的数据。

    利用条件

    1.本质上还是Java原生的反序列化利用,所以需要环境中有可用的Gadget;

    2.需要能伪造相关系统表的数据,将“SHOW SESSION STATUS”的执行结果设置为我们精心构造的反序列化数据,或者基于mysql连接协议,自定义返回数据,后面有时间的时候会写写这块儿;

    3.可控的JDBC URL;

  • 相关阅读:
    字符串匹配算法之Rabin-Karp算法
    算法导论之最近顶点对
    php连mssql中文乱码问题
    Trie树
    PAT 1057. Stack (30)
    PAT 1033. To Fill or Not to Fill (25)
    PAT 1034. Head of a Gang (30)
    PAT 1075. PAT Judge (25)
    spring框架资料
    Spring Security资料
  • 原文地址:https://www.cnblogs.com/Welk1n/p/12056097.html
Copyright © 2020-2023  润新知