zoukankan      html  css  js  c++  java
  • 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;

  • 相关阅读:
    极光推送JPush
    dom4j解析xml
    Hibernate4之JPA规范配置详解
    Jquery的$命名冲突
    初识zookeeper(二)之与Dubbo-admin关联
    初识zookeeper(一)之zookeeper的安装及配置
    jenkins maven svn 部署web项目到本地Tomcat
    spring mvc 和junit 4集成的注意点
    String 和 CharSequence 关系与区别
    (转)如何安装 easy installer+pip
  • 原文地址:https://www.cnblogs.com/Welk1n/p/12056097.html
Copyright © 2011-2022 走看看