、小白都能看懂的JSON反序列化远程命令执行
作者:CQITer小编 时间:2018-03-25 09:57
Fastjson是一个由阿里巴巴维护的一个json库。它采用一种“假定有序快速匹配”的算法,是号称Java中最快的josn库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。今天我们就以最详细的姿势,一步步分析一下FastJson的远程命令执行!
0×01序列化先熟悉一下FastJson的用法,毕竟连用法都不会怎么分析漏洞。下面用在最简单的示例快速入门一下FastJson

简单创建了一个实体bean,并set了两个属性值。进行简单的序列化,看看序列化后是不是变成了我们想要的东西。至于WriteClassName的作用,序列化时写入类型信息,默认为false。反序列化是需用到。

此时,已经非常完美的序列化成了我们常见的json数据。而加了WriteClassName属性的序列化,多了一个@type,也就是我们当时创建的那个实体对象。
0×02反序列化反序列化的用法也比较简单,也就是将toJSONString换成parseObject即可。第一个参数是json字符串,第二个参数就是前面说到的@type实体对象。

成功的将字符反序列化位了实体对象。

分析漏洞最好的方式就是看看他到底做了什么防御,从他的补丁入手。


从更新的补丁来看,官方增加了一个checkAutoType方法,看到check这个词大概就能想到这块估计是是做了一个黑名单。跟进这个方法

发现checkAutoType方法对denyList列表进行了便利。跟进checkAutoType看一看。
public Class<?> checkAutoType(StringtypeName, Class<?> expectClass) {
if (typeName == null) {
return null;
}
final String className = typeName.replace('$', '.');
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)){
returnTypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)){
throw newJSONException("autoType is not support. " + typeName);
}
}
}
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}
if (clazz != null) {
if (expectClass != null && !expectClass.isAssignableFrom(clazz)){
throw newJSONException("type not match. " + typeName + " -> " +expectClass.getName());
}
return clazz;
}
if (!autoTypeSupport) {
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)){
throw newJSONException("autoType is not support. " + typeName);
}
}
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if(className.startsWith(accept)) {
clazz =TypeUtils.loadClass(typeName, defaultClassLoader);
if (expectClass != null&& expectClass.isAssignableFrom(clazz)) {
throw newJSONException("type not match. " + typeName + " -> " +expectClass.getName());
}
return clazz;
}
}
}
if (autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
}
if (clazz != null) {
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
||DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw newJSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if(expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw newJSONException("type not match. " + typeName + " -> " +expectClass.getName());
}
}
}
if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " +typeName);
}
return clazz;
}
}
首先他会先判断expectClass是否为空如果为空的化,就会去检查denyList这个列表。看名字就知道是一个黑名单列表。看看这个列表里都有什么东西。



