Jackson gadgets - 漏洞剖析
22 Jul 2019 - 发布者:Andrea Brancaleoni
Jackson CVE-2019-12384:一类漏洞的剖析
在一次安全评估中,我们分析了一个使用Jackson库进行JSON反序列化的应用程序。在该场景下,我们发现了一个反序列化漏洞,攻击者可以控制要反序列化的类。本文将展示攻击者如何利用此反序列化漏洞触发服务端请求伪造(SSRF)和远程代码执行等攻击。
这项研究还导致了一个新的CVE-2019-12384漏洞的发现,以及一系列受影响的RedHat产品:
必要条件
正如Jackson作者在《关于Jackson CVE:不要恐慌——你需要知道的事情》中指出的,Jackson “gadget"漏洞的要求如下:
- 应用程序接受由不受信任的客户端发送的JSON内容(手动组合或由您未编写且无法控制和查看的代码生成)——这意味着您无法约束正在发送的JSON本身。
- 应用程序对名义类型为
java.lang.Object(或少数"宽松"标记接口,如java.util.Serializable、java.util.Comparable)的属性使用多态类型处理。 - Java类路径中至少存在一个可供利用的特定"gadget"类。具体来说,利用需要一个能与Jackson配合使用的类。实际上,大多数gadget只适用于特定的库——例如,最常报告的gadget适用于JDK序列化。
- 应用程序使用的Jackson版本尚未阻止该特定的"gadget"类。已公开的gadget集合随着时间的推移而增长,因此这是一场发现和报告gadget与发布补丁之间的竞赛。Jackson采用黑名单机制。反序列化是平台的一个"特性”,他们持续更新已知gadget的黑名单。
在本研究中,我们假设前提条件(1)和(2)已满足。相反,我们专注于寻找能够同时满足(3)和(4)的gadget。请注意,Jackson是Java应用程序中最常用的反序列化框架之一,其中多态性是一等概念。潜在攻击者可以使用静态分析工具或其他动态技术(例如在请求/响应中搜索@class)来发现这些条件,成本几乎为零。
战场准备
在研究过程中,我们开发了一个工具来辅助发现此类漏洞。当Jackson反序列化ch.qos.logback.core.db.DriverManagerConnectionSource时,此类可被滥用来实例化一个JDBC连接。JDBC代表Java数据库连接。JDBC是用于连接数据库并执行查询的Java API,是JavaSE的一部分。此外,JDBC使用自动的字符串到类的映射,因此它是加载和执行链中更多"gadget"的完美目标。
为了演示攻击,我们准备了一个包装器,在其中加载攻击者指定的任意多态类。对于环境,我们使用了jRuby,这是一个运行在Java虚拟机(JVM)之上的Ruby实现。凭借其在JVM之上的集成,我们可以轻松加载和实例化Java类。
我们将使用此设置轻松加载给定目录中的Java类,并准备Jackson环境以满足上述前两个要求(1,2)。为此,我们实现了以下jRuby脚本。
|
|
脚本流程如下:
- 第2行,加载"classpath"子目录下所有JAR文件中的类
- 第5至13行,配置Jackson以满足要求(#2)
- 第14至17行,将传递给jRuby的JSON多态Jackson对象进行反序列化和序列化
关键步骤:触及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
需要注意的是,h2库不是执行SSRF所必需的,因为我们的经验表明,大多数Java应用程序至少加载一个JDBC驱动程序。JDBC驱动程序是类,当传入JDBC URL时,它们会自动实例化,并将完整的URL作为参数传递给它们。
使用以下命令,我们将使用上述类路径调用之前的脚本。
|
|
在脚本的第15行,Jackson将使用子对象中包含的键递归调用所有setter方法。更具体地说,setUrl(String url)方法被Jackson反射库调用并传入参数。在该阶段之后(第17行),完整对象再次被序列化为JSON对象。此时,所有字段都直接序列化(如果未定义getter),或通过显式getter序列化。对我们来说,感兴趣的getter是getConnection()。
实际上,作为攻击者,我们感兴趣的是所有具有有趣副作用且我们能控制参数的"非纯"方法。当调用getConnection时,会实例化一个内存数据库。由于应用程序生命周期很短,从攻击者的角度来看,我们不会看到任何有意义的效果。为了做更有意义的事情,我们创建一个到远程数据库的连接。如果目标应用程序部署为远程服务,攻击者可以产生服务端请求伪造(SSRF)。以下截图是此场景的示例。
进入矩阵:从SSRF到RCE
您可能已经注意到,这两种情况都会导致拒绝服务(DoS)和SSRF。虽然这些攻击可能会影响应用程序安全性,但我们想向您展示一种简单有效的技术,将SSRF转变为完整的RCE攻击链。
为了在应用程序上下文中获得完整的代码执行能力,我们利用了加载H2 JDBC驱动程序的能力。H2是一个超快速的SQL数据库,通常用作完整SQL数据库管理系统(如Postgresql、MSSql、MySql或OracleDB)的内存替代品。它易于配置,并且实际上支持多种模式,如内存模式、文件模式和远程服务器模式。H2具有从JDBC URL运行SQL脚本的能力,添加此功能是为了让内存数据库支持初始化迁移。
仅凭这一点,攻击者还无法在JVM上下文中实际执行Java代码。然而,H2由于是在JVM内部实现的,具有指定包含Java代码的自定义别名的能力。这正是我们可以滥用来执行任意代码的地方。
我们可以通过一个简单的HTTP服务器(例如python -m SimpleHttpServer)轻松提供以下inject.sql INIT文件。
|
|
并运行测试程序:
|
|
成功!
迭代式污点跟踪
利用反序列化漏洞是复杂且耗时的。在进行产品安全审查时,时间限制可能使得难以找到合适的gadget进行利用。另一方面,Jackson黑名单每月更新,而使用此机制的用户(例如企业应用程序)可能具有年度发布周期。
反序列化漏洞是典型的大海捞针问题。一方面,识别易受攻击的入口点是一项容易的任务,而寻找有用的gadget可能既耗时又繁琐。
在Doyensec,我们开发了一种技术来寻找有用的Jackson gadget,以方便后一项工作。我们构建了一个静态分析工具,可以通过污点跟踪分析来查找序列化gadget。我们将其设计得足够快,以便能够运行多次,并通过自定义和可扩展的规则集语言进行迭代/改进。平均而言,在2018款Macbook PRO i7上运行一次大约需要2分钟。
污点跟踪是一个热门的学术研究主题。学术研究工具专注于非常高的召回率和精确率。权衡在于高召回率/精确率与速度/内存之间。由于我们希望该工具在测试商业级产品时可用,并且我们重视工具本身的可定制性,因此我们专注于速度和可用性,而不是高召回率。虽然该工具的灵感来自其他研究(如flowdroid),但我们的技术重点不是排除人工分析员。相反,我们相信通过可定制的安全自动化来增强手动测试和利用能力。
这项研究得益于Doyensec提供的25%研究时间。请继续关注新内容。
就到这里了!保持警惕,注意安全!