作者: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
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1481/
来源:oschina
链接:https://my.oschina.net/u/4326858/blog/4948041