一、漏洞简介
Jackson是一个广泛使用的Java库,用于将JSON数据与Java对象相互转换。由于其高效且易于使用,Jackson已成为许多Java应用程序的首选序列化和反序列化库。然而,Jackson在处理反序列化时存在安全漏洞,攻击者可以通过精心构造的JSON数据利用这些漏洞进行注入攻击,进而窃取机密信息或远程执行恶意代码。这类安全漏洞称为Jackson反序列化漏洞。
二、漏洞分类
根据CVE漏洞分类标准,Jackson反序列化漏洞可以分为以下三类:
1.远程代码执行漏洞
攻击者可以构造恶意JSON数据,通过反序列化触发远程代码执行漏洞,从而在受害者计算机上执行任意恶意代码。
2.路径遍历漏洞
攻击者可以构造恶意JSON数据,通过反序列化触发路径遍历漏洞,窃取应用程序的敏感信息或配置文件。
3.反射注入漏洞
在某些情况下,攻击者可以构造恶意JSON数据,通过反序列化触发反射注入漏洞,从而使得攻击者可以在受害者计算机上执行恶意代码。
三、漏洞成因
Jackson反序列化漏洞的根本原因是Java反序列化机制的缺陷。在Java中,对象可以在网络上或磁盘上进行序列化,将其转换为字节流。序列化使得我们可以将对象转换为流,以便在不同的应用程序之间共享。反序列化是在收到字节流时将其转换回Java对象。Jackson库使用Java中的反射机制实现对象的反序列化,反射机制通过访问对象的内部信息使得对象可以动态地创建、修改和处理,但它的缺陷也非常突出,这些缺陷为Jackson反序列化漏洞的产生提供了基础。
四、漏洞验证与漏洞实例分析
1.漏洞验证
下面是一个漏洞验证用例,攻击者通过恶意构造JSON数据触发远程代码执行漏洞,在受害者计算机上执行命令:
import com.fasterxml.jackson.databind.ObjectMapper; public class Test { public static void main(String[] args) throws Exception { String json = "{\"@class\":\"com.test.Exploit\",\"name\":\"test\",\"command\":\"calc.exe\"}"; ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.readValue(json, Object.class); } }
在这个例子中,攻击者构建了一个名为Exploit的Java类,并将命令calc.exe作为Exploit对象的属性值,然后使用ObjectMapper将其转换为JSON字符串。接着,攻击者使用ObjectMapper反序列化JSON字符串,由于ObjectMapper启用了默认类型序列化,攻击者的Exploit类和calc.exe命令将在受害者计算机上执行。
2.漏洞实例分析
以下是一个实际情况下的漏洞案例分析,通过该案例我们可以深入了解漏洞产生的原因以及防御该漏洞的方法。
在Springfox-swagger2库中使用的Jackson-databind库存在反序列化漏洞。具体来说,在Springfox-swagger2库的2.7.0版本以及之前的版本中,假设攻击者能够成功向服务器端注入恶意对象,将可导致RCE漏洞。该漏洞的触发条件如下:
攻击者可以构造一个JSON字符串,其中包含一个表示日期的属性,值为相应类型的JSON。由于在转换Java类型之前,Jackson-databind库没有正确验证输入的类型,攻击者可以通过构造适当的JSON类型来利用反序列化漏洞。
public class Exploit { public Exploit() throws Exception { // run calc.exe final String[] calc = new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}; Runtime.getRuntime().exec(calc); } }
在上面的代码中,我们定义了一个名为Exploit的类,并在构造函数中执行了calc.exe命令。这个Exploit的类声明一个恶意类的成员,在编写Jackson反序列化漏洞利用代码时,这个Exploit类将被用于向服务器送入一个用于反序列化的有效JSON字符串,以实现执行计算器应用程序的目的。
{ "date": { "@class": "com.exploit.Exploit" } }
攻击者可以通过使用下面的JSON字符串将有效Java对象注入受害者的服务器端,从而触发攻击:
{ "date": { "@class": "com.exploit.Exploit" } }
在上面的JSON字符串中,我们定义了一个JSON对象date,其中包含一个@class属性,值为Exploit类的名称,这是 Jackson-databind库的一个特殊属性,能够自动进行类型解析和反序列化操作,因此,对于指定的类名称,Jackson-databind库将创建一个对象并将其初始化为注入的值,这是漏洞利用的重点。这个对象的初始化将调用Exploit类的构造函数,从而执行命令执行calc.exe程序。
五、漏洞修复
可以通过以下方式防止此类漏洞的出现:
1.关闭Jackson的默认类型序列化功能
关闭Jackson的默认类型序列化功能可以消除反序列化漏洞,这样可以忽略来自不信任源的数据。请注意,关闭默认类型序列化功能可能导致某些应用程序出现不可预测的行为。
ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); //关闭默认类型序列化功能 mapper.disableDefaultTyping();
2.限制反序列化的输入
可以通过Java Security Manager限制反序列化操作的输入,防止攻击者构造恶意JSON数据来触发序列化攻击。
SecurityManager sm = new SecurityManager(); Policy.setPolicy(new Policy() { @Override public PermissionCollection getPermissions(CodeSource codesource) { return new Permissions(); } }); System.setSecurityManager(sm);
3.使用Jackson的注解
开发人员使用Jackson的注解可以使反序列化过程受到更多的控制,从而避免误用对象反序列化从而导致安全漏洞。
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat")}) public abstract class Animal { //... }
六、总结
本文深入介绍了Jackson反序列化漏洞的原因、分类及成因,并通过漏洞实例分析详细讲解了漏洞利用方法。最后,介绍了三种防范和修复该漏洞的方法,开发人员应该充分了解这些方法,并在编写代码时遵守相应的安全规则,以避免安全问题。