Skip to content

Latest commit

 

History

History
201 lines (167 loc) · 7.72 KB

CVE-2017-18349.md

File metadata and controls

201 lines (167 loc) · 7.72 KB

Fastjson 1.2.24 反序列化漏洞分析 (CVE-2017-18349)

0x00 Introduction

国内经典的java反序列化漏洞利用大杀器。使用量大,漏洞频出。最后开发者甚至禁用fastjson组件。

环境准备

  • jdk 1.8
  • fastjson 1.2.24

Fastjson是java的一个库,可以将Java对象转化为json格式的字符串,也可以将json格式的字符串转化为Java对象。

0x01 漏洞分析

基本情况

Fastjson反序列化函数:

  • com.alibaba.fastjson.JSON.parseObject 返回JSONObject类型
  • com.alibaba.fastjson.JSON.parse 返回实际类型对象

反序列化操作触发函数(类似PHP魔法函数):构造方法、getter和setter方法都被调用了,所以构造恶意类的时候,只要使用fastjson反序列化的话就可以调用getter或者setter中的恶意方法了。

为什么反序列化会触发setter,getter?——@type特性

执行反序列化的根源定位在 @type

fastjson通过指定@type的值来实现定位某类,而这种方法进行反序列化,会执行类的构造方法和属性相关的get,set方法,也造成了这个漏洞的产生。

poc,@type特性调用自定义的恶意类ExecPoc的setCmdxxx方法,参数是calc.exe:

String poc = "{\"@type\":\"ExecPoC\",\"cmdxxx\":\"calc.exe\"}";

JSONObject b = JSON.parseObject(poc);

调试分析

动态调试,寻找原因。

com.alibaba.fastjson.JSON类中,新建DefaultJSONParser对象会对json数据进行初步解析构造。

public static Object parse(String text, int features) {
    if (text == null) {
        return null;
    } else {
        DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
        Object value = parser.parse();
        parser.handleResovleTask(value);
        parser.close();
        return value;
    }
}

com.alibaba.fastjson.parser.DefaultJSONParser构造函数中,进行json字符串的解析,{token为12,[的token为14,以此区分json数据对象是不是数组。

public DefaultJSONParser(Object input, JSONLexer lexer, ParserConfig config) {
    ...
    if (ch == '{') {
        lexer.next();
        ((JSONLexerBase)lexer).token = 12;
    } else if (ch == '[') {
        lexer.next();
        ((JSONLexerBase)lexer).token = 14;
    } else {
        lexer.nextToken();
    }

}

DefaultJSONParser类的parse()方法,根据不同token也就是{[进行处理。

case 12:
    JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
    return this.parseObject((Map)object, fieldName);
case 14:
    JSONArray array = new JSONArray();
    this.parseArray((Collection)array, (Object)fieldName);
    if (lexer.isEnabled(Feature.UseObjectArray)) {
        return array.toArray();
    }

    return array;

这开始关键了, DefaultJSONParser类的parseObject()方法 获取json字符串第一个键名为@type。后面有一个对key的判断,进入到这个if分支里。

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
    ref = lexer.scanSymbol(this.symbolTable, '"');
    Class<?> clazz = TypeUtils.loadClass(ref, this.config.getDefaultClassLoader());
    if (clazz != null) {
        lexer.nextToken(16);
        if (lexer.token() == 13) {...}
        this.setResolveStatus(2);
        if (this.context != null && !(fieldName instanceof Integer)) {
            this.popContext();
        }

        if (object.size() > 0) {
            instance = TypeUtils.cast(object, clazz, this.config);
            this.parseObject(instance);
            thisObj = instance;
            return thisObj;
        }

        ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
        //反序列化点
        thisObj = deserializer.deserialze(this, clazz, fieldName);
        return thisObj;

com.alibaba.fastjson.parser.ParserConfig中会通过createJavaBeanDeserializer方法创建一个bean反序列化对象。

if (derializer != null) {
    return (ObjectDeserializer)derializer;
} else {
    if (clazz.isEnum()) {
        derializer = new EnumDeserializer(clazz);
    } else if (clazz.isArray()) {
        derializer = ObjectArrayCodec.instance;
    } else if (clazz != Set.class && clazz != HashSet.class && clazz != Collection.class && clazz != List.class && clazz != ArrayList.class) {
        if (Collection.class.isAssignableFrom(clazz)) {
            derializer = CollectionCodec.instance;
        } else if (Map.class.isAssignableFrom(clazz)) {
            derializer = MapDeserializer.instance;
        } else if (Throwable.class.isAssignableFrom(clazz)) {
            derializer = new ThrowableDeserializer(this, clazz);
        } else {
            derializer = this.createJavaBeanDeserializer(clazz, (Type)type);
        }

继续跟进到com.alibaba.fastjson.util.JavaBeanInfo的build,会检测@type字段指定的类的成员方法,将符合要求的setter和getter加入fieldList队列里。

Method[] var30 = methods;
int var29 = methods.length;

Method method;
 for(i = 0; i < var29; ++i) {
    method = var30[i];
    ordinal = 0;
    int serialzeFeatures = 0;
    parserFeatures = 0;
    String methodName = method.getName();
    //符合要求的setter
    if (methodName.length() >= 4 && !Modifier.isStatic(method.getModifiers()) && (method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(method.getDeclaringClass()))) {
        Class<?>[] types = method.getParameterTypes();
        ...
    }
    //符合要求的getter
    if (methodName.length() >= 4 && !Modifier.isStatic(method.getModifiers()) && methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3)) && method.getParameterTypes().length == 0 && (Collection.class.isAssignableFrom(method.getReturnType()) || Map.class.isAssignableFrom(method.getReturnType()) || AtomicBoolean.class == method.getReturnType() || AtomicInteger.class == method.getReturnType() || AtomicLong.class == method.getReturnType())) {
        ...
    }
    ...
    //符合setter和getter添加到filedList(com.alibaba.fastjson.util.FieldInfo)
    add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures, annotation, fieldAnnotation, (String)null));

setter要求:

  1. 方法名长度不小于4(满足javabean规范的方法)
  2. 不能是静态方法
  3. 返回值是void
  4. 传入的参数个数为1
  5. 方法名为set打头

getter要求:

  1. 方法名长度不小于4
  2. 不能是静态方法
  3. 方法名要 get 开头同时第四个字符串要大写
  4. 方法返回的类型必须继承自 Collection Map AtomicBoolean AtomicInteger AtomicLong
  5. 传入的参数个数需要为 0

最后在com.alibaba.fastjson.parser.deserializer.FieldDeserializer的setValue调用method.invoke(object, value);触发反射调用类setter设置参数并执行。
调用链:

setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:593, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
parseObject:201, JSON (com.alibaba.fastjson)
main:9, PoC

getter是在setter之后执行。

Ref