Apache Druid 远程代码执行漏洞分析(CVE-2021-25646)

感情迁移 提交于 2021-02-06 10:25:35

作者:Skay@QAX A-TEAM
原文链接:https://mp.weixin.qq.com/s/m7WLwJX-566WQ29Tuv7dtg

一、调试环境

https://archive.apache.org/dist/druid/0.20.1/

这里尝试了几种常规的调试方法都不行,然后看到conf目录下存在jvm.config,一搜好多,因为我们的启动脚本为start-micro-quickstart,所以最后范围锁定在这几个

图片

一开始踩了一个坑,D:\java\druid\druid run\apache-druid-0.20.0\conf\druid\single-server\micro-quickstart\router\runtime.properties 中我看到了8888端口,这恰恰也是我们的web服务端口,下意识就改了这个jvm.config

图片

某些断点可以停住,但是我们漏洞出发点并不行,后来经过Smi1e师傅的提示,需要我改错了

图片

在此感谢~

二、漏洞分析

往上分析文章就只有阿里发出的,拿出上学的时候做阅读理解的本事,使劲看吧....

1.定位JavaScriptDimFIlter

阿里爸爸文章一开始就提出了利用的关键类JavaScriptDimFilter,反思一下,因为对Java相关的JavaScript知识储备几乎是0,看到fasterxml就一心想着反序列化了.........

这里简单提一下Javascript,Druid支持在运行时动态注入JavaScript,且不会在沙箱中执行Java脚本,因此它们具有对计算机的完全访问权限。因此,JavaScript函数允许用户在druid进程内执行任意代码。因此,默认情况下,JavaScript是禁用的。但是,在开发/登台环境或受保护的生产环境中,可以通过设置configuration属性 来启用它们druid.javascript.enabled = true。

这里提到了关于漏洞的关键点:druid.javascript.enabled,留个坑,稍后提到。

2.关于JsonCreator注解、JsonProperty注解、以及CreatorProperty类型

文章中提到了两个注解,以及一个类(参数类型),这里需要去补一下Jackson的一些知识了

@JasonCreator

该注解用在对象的反序列时指定特定的构造函数或者工厂方法。在反序列化时,Jackson默认会调用对象的无参构造函数,如果我们不定义任何构造函数,Jvm会负责生成默认的无参构造函数。但是如果我们定义了构造函数,并且没有提供无参构造函数时,Jackson会报错 再回到@JsonCreator注解,其作用就是,指定对象反序列化时的构造函数或者工厂方法,如果默认构造函数无法满足需求,或者说我们需要在构造对象时做一些特殊逻辑,可以使用该注解。该注解需要搭配@JsonProperty使用

@JsonProperty

此注解作用于属性上,作用是把该属性的名称序列化成另一个自己想要的名称 对属性名进行重命名,在java里我们墨守规定驼峰命名,但是在一些特殊的场合下,比如数据库是下划线等,再此我们就可以进行映射 对属性名称重命名,比如在很多场景下Java对象的属性是按照规范的驼峰书写,但在数据库设计时使用的是下划线连接方式,此处在进行映射的时候

有了对注解的理解,再来看这句话,以及JavaScriptDimFilter的构造函数

图片

图片

JavaScriptDimFilter的构造函数是用JasonCreator修饰的,也就说JavaScriptDimFilter这个类在反序列化(这里指的是从Json数据转化为对象)时,Jackson会调用这个构造方法,且由于dimesion、function、extractionFn、filterTuning都有@JasonProperty注解修饰,Jackson在在反序列化处理解析到JavaScriptDimFilter时,都会被封装为CreatorProterty类型,而对于没有被标记@JasonProperty的config参数,会创建一个name为””的CreatorProperty

图片

跟到这里提出了一个疑问,Jackson是怎样将org.apache.druid.js.JavaScriptConfig注入到里面的?留坑,稍后回答。

图片

3.Jackson解析用户输入

(1) Jersey的初始配置注入部分

1.HTTP Server端采用的是Jersey框架,所有的配置信息都由Guice框架在启动的时候进行绑定注入,比如利用的JavaScriptConfig,初始化的时候读取配置文件中的druid.javascript.enabled绑定到JavaScriptConfig的enabled field,这部分是非本地用户不可控的。

对应了上文提到的druid.javascript.enabled,我尝试在druid中的配置文件找这个开关的配置,反编译后全局搜javascript关键字都没找到,后来请教了下Litch1师傅,说这个是默认配置,没有具体去跟,具体可以看下有个test测试里有这部分:

图片

(2) Jackson获取对应的creatorProperty(JavaScriptConfig)

文中直接定位到了com.fasterxml.jackson.databind.deser.BeanDeserializer#_deserializeUsingPropertyBased方法

会拿解析到的json串中的“键名”去查找当前解析对象中对应的creatorProperty,这步对应的是findCreatorProperty方法,findCreatorProperty方法会去_propertyLookup 这个HashMap中查找”键名”对应的属性,在_propertyLookup中可以看到其中没有用JsonProperty注释修饰的JavaScriptConfig的键为””,要是json串中的键也为””,就能匹配上,取出JavaScriptConfig对应的creatorProperty

跟着文章将视线集中到com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator#findCreatorProperty(java.lang.String)

图片

可以看到它在一个for循环中,for循环遍历了p(JsonParser,这里稍微不准确的理解为存储了待处理的Json数据),按照Json层级结构一层一层的处理Json数据,因为payload较长,快进到“”处理逻辑,从P中取到propName,然后进入findCreatorProperty

图片

this._propertyLookup是个数组,直接取到我们想要的JavaScriptConfig对应的creatorProperty

图片

还是之前留的坑,这个Hashmap是怎么初始化的?Jackson是怎样将org.apache.druid.js.JavaScriptConfig注入到里面的?这里仔细跟一下

图片

这个this,也就是creator

图片

图片

这里仔细跟下调用栈跳到上一步

deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase,Hashmap被存在了((BeanDeserializer)this)._propertyBasedCreator中

图片

再往上,deserializeFromObject:326, BeanDeserializer 还是this._propertyBasedCreator,也就是BeanDeserializer的属性

图片

一直向上跟进,看到了BeanDeserializer调用deserialize方法

可以看到这里的deser中已经初始化完毕了_propertyLookup Hashmap

图片

再去看deser的初始化情况

图片

其实跟到这里就想停了,因为payload里我们给的type是javascript,反序列化时会Jackson会解析到相应的实体类也就是JavaScriptDimFilter

图片

继续走跟到com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase#_findDeserializer中

图片

经过一层层的变量传递,来到了deserializeTypedFromObject:97, AsPropertyTypeDeserializer

图片

deserialize:527, SettableBeanProperty 这时这个hashmap 被赋值到了SettableBeanProperty的_valueTypeDeserializer参数上

图片

然后向前追踪SettableBeanProperty,最终又回到了BeanDeserializer

图片

可以看到creatorProp通过findCreatorProperty中获得

图片

又回来了.....这时候就懵逼了一会儿,不过感谢idea

图片

_deserializeUsingPropertyBased:417, BeanDeserializer 被调用了四次,回头看下我们的payload Json数据是四层,嗯,对上了....大概是這個樣子的吧.....这里有点懵

这里放一下部分调用栈吧,

_findDeserializer:198, TypeDeserializerBase (com.fasterxml.jackson.databind.jsontype.impl)
_deserializeTypedForId:113, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:97, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:527, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [4]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [3]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [2]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:530, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
_deserializeWithErrorWrapping:528, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeUsingPropertyBased:417, BeanDeserializer (com.fasterxml.jackson.databind.deser) [1]
deserializeFromObjectUsingNonDefault:1287, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
deserializeFromObject:326, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeOther:194, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:161, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeTypedForId:130, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:97, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:68, TypeWrappedDeserializer (com.fasterxml.jackson.databind.deser.impl)
_bind:1682, ObjectReader (com.fasterxml.jackson.databind)
readValue:977, ObjectReader (com.fasterxml.jackson.databind)
readFrom:814, ProviderBase (com.fasterxml.jackson.jaxrs.base)
getEntity:490, ContainerRequest (com.sun.jersey.spi.container)
getValue:123, EntityParamDispatchProvider$EntityInjectable (com.sun.jersey.server.impl.model.method.dispatch)
getInjectableValues:86, InjectableValuesProvider

(3) 反序列化相应参数

拿到对应的creatorProperty之后就会将用户输入的json串中这个键对应的根据类型去反序列相应的参数,

图片

(4) 触发漏洞

最后漏洞的利用点就是利用config为true之后绕过了对于config的检查

图片

然后进行JavaScript的执行。

三、总结一下

这个漏洞主要就是根据Jackson解析特性(解析name为""时)会将value值绑定到对象(JavaScriptDimFilter,type为javascript时指定的)的对应参数(config)上,造成JavaScriptDimFilter中function属性中的javascript代码被执行。

四、补丁分析

最新版本payload会报错

图片

官方修复

图片

看下调试过程this._propertyLookup hashmap中只剩下了四个

图片

直接干掉了""对应的解析,这就阻断了JavaScriptConfig对应的creatorProperty生成,hashmap中将不存在JavaScriptConfig,后面的链也就断了.....无法打开JavaScript执行开关。

其实问了下洞主,说实际上还应该跟下具体Jackson执行逻辑具体是怎么修复的,但是那个递归搞得我太头疼了,暂时先这样吧....

五、参考链接

https://mp.weixin.qq.com/s/McAoLfyf_tgFIfGTAoRCiw

https://druid.apache.org/docs/0.19.0/tutorials/index.html

https://blog.csdn.net/u010900754/article/details/105859959

PS:感谢Litch1 和 Smi1e 指点....wtcl


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1481/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!