Skip to content

Latest commit

 

History

History
103 lines (77 loc) · 9.55 KB

File metadata and controls

103 lines (77 loc) · 9.55 KB

H2 JDBC URL Java命令执行2(H2JavaExecJdbc2)

  • 类别common 别名:(无) 优先级:10
  • 作者:Unam4、Springkill
  • 依赖com.h2database:h2:org.h2.DriverAll JDk(不挑 JDK 版本)
  • 实现Gadget(非反序列化跳板,而是 JDBC URL 注入型 sink)

作用

面向 JDBC URL 注入 / JDBC RCE 场景:当目标应用会用攻击者可控的 JDBC 连接字符串去连接数据库时,本 gadget 生成一段恶意的 H2 内存库连接 URL。该 URL 借助 H2 的 init(连接建立时执行的初始化 SQL)参数,CREATE TRIGGER 定义一段内联 Java 源码,H2 在建连过程中即时 编译并执行该 Java,最终以 Runtime.getRuntime().exec 直接执行系统命令。相较字节码加载类 gadget(如 H2JavaJdbc2 系列走 javac + 任意字节码),本 gadget 的 payload 更短、只做命令执行,故命名「命令执行2」。

它是链的 最尾端 sink(END:自身产出最终字符串 payload(JDBC URL),不再包装任何更内层对象,chain 参数在 invoke 中未被使用。

链接标签

  • 入口 tagsH2JdbcUrlJdbcUrlChainsEND
    • JdbcUrlChains:声明「我是一条 JDBC URL 型利用」,由 JDBC URL 注入类的 Payload 入口 / 上游注入点承接(如 Fastjson/Jackson 触发 DataSource.getConnection、C3P0、HikariCP 等把可控 URL 传入 DriverManager.getConnection 的场景)。
    • H2JdbcUrl:进一步限定为 H2 驱动专用的 URL(依赖 org.h2.Driver)。
    • END:链尾终结标签,表示本 gadget 自身即产出最终 payload,其后不再有下一环。
  • 衔接 nextTags:(空)—— 作为 END 节点,不向后衔接任何 gadget。
  • excludes:(空)。

在链中的角色:sink / 链尾。上游只需提供「把这段 URL 交给 H2 驱动去连接」的能力,本 gadget 负责把命令包装进 H2 可执行的 SQL/Java。

源码剖析

全类仅两个方法,逻辑集中在 getObject()

@GadgetTags(tags={"H2JdbcUrl", "JdbcUrlChains", "END"})
@GadgetAnnotation(name="H2 JDBC URL Java命令执行2", description="...Payload比较简短", dependencies={"com.h2database:h2:org.h2.Driver", "All JDk"}, authors={"Unam4", "Springkill"}, priority=10)
public class H2JavaExecJdbc2 implements Gadget {
    @Param(name="命令", description="eg: cmd /c calc")
    public String cmd = "cmd /c calc";
    public String driverClassName = "org.h2.Driver";

    public String getObject() {
        String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER UNAM4 BEFORE SELECT ON\nINFORMATION_SCHEMA.TABLES AS $$ void UNAM4() throws Exception{ Runtime.getRuntime().exec(\"" + this.cmd + "\")\\;}$$";
        return url;
    }

    @Override
    public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
        context.put(ContextTag.DRIVER_CLASS_NAME_KEY, this.driverClassName);
        return this.getObject();
    }
}

逐段解释:

  • jdbc:h2:mem:test:连接一个名为 test内存态 H2 数据库。内存库无需磁盘、无需鉴权即可创建,连接动作本身就足以触发后续 init 逻辑,是 H2 JDBC 攻击的经典载体。
  • MODE=MSSQLServer:切换 H2 的兼容模式。设置该 MODE 影响 H2 对建连后元数据查询、语法解析的行为,配合 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES 触发器保证在连接建立阶段就有一次对系统表 INFORMATION_SCHEMA.TABLES 的自动 SELECT,从而在 无需显式执行任何业务 SQL 的情况下引爆触发器。
  • init=...:H2 的连接参数,其值是一段在连接建立时由 H2 自动执行的初始化 SQL。这是整个利用的入口——攻击者无需应用主动执行查询,仅「连上这个库」即可。
  • CREATE TRIGGER UNAM4 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$ void UNAM4() throws Exception{ Runtime.getRuntime().exec("<cmd>")\;}$$
    • H2 支持以 $$ ... $$ 包裹一段 内联 Java 源码 定义触发器实现,建连时 H2 会用内置编译器把这段 Java 编译成类并加载。
    • 触发器绑定在 INFORMATION_SCHEMA.TABLESBEFORE SELECT 上:H2 在建连与元数据交互过程中会自动读取该系统表,触发器随即被调用,内联的 UNAM4() 方法执行 Runtime.getRuntime().exec(cmd) 完成命令执行。
    • exec(...)\; 中的 \;(源码字符串里的 \\;)是对分号的转义:H2 在解析 init/SQL 时以 ; 作为语句分隔符,Java 源码体内部的 ; 必须转义为 \;,否则会被 H2 提前切断语句,导致触发器体不完整。
    • 方法体内的换行 ON\nINFORMATION_SCHEMA.TABLES(源码里的 \n)仅是 SQL 中的换行,无害。
  • invoke():把驱动类名 org.h2.Driver 写入 GadgetContextContextTag.DRIVER_CLASS_NAME_KEY),供上游 Payload 组装时知道该用哪个 JDBC 驱动加载这条 URL;随后返回 getObject() 生成的 URL 字符串作为本链产物。注意 chain 参数未被使用——印证其 END 定位:没有更内层对象需要 chain.doCreate 取出。

H2JavaExecJdbc1 的差异H2JavaExecJdbc1INIT=CREATE ALIAS EXEC AS 'void cmd_exec...' + 显式 CALL EXEC(...)(存储别名 + 主动调用),需要触发一次别名调用;本 Jdbc2 改用 TRIGGER BEFORE SELECT ON INFORMATION_SCHEMA.TABLES,把执行时机挂到建连必然发生的系统表读取上,因而不依赖任何显式业务查询,隐蔽性与通用性更好,且标注 All JDk(不限 JDK 版本,因为只用最基础的 Runtime.exec,不涉及高版本受限的反射/字节码 API)。

参数(@Param)

字段 名称 说明 类型 默认值 可选值
cmd 命令 要执行的系统命令,直接拼入内联 Java 的 Runtime.getRuntime().exec("...")。示例:cmd /c calc String cmd /c calc 无(自由填写)

另有公开字段 driverClassName(默认 org.h2.Driver),非 @Param,一般无需改动;在 invoke 中写入上下文 DRIVER_CLASS_NAME_KEY

注意 cmd原样字符串拼接进 URL 与 Java 源码,未做转义。含空格的命令由 Runtime.exec(String) 按空白切分参数(非 shell 解析),需要 shell 特性时应显式用 cmd /c .../bin/sh -c ... 形式;若 cmd 中包含 ";$$ 等字符会破坏 URL/触发器语法。

适用版本与原理要点

  • 数据库:需目标侧存在 H2 驱动(com.h2database:h2org.h2.Driver)。H2 的 init/INIT 连接参数 + CREATE TRIGGER ... AS $$ java $$ 内联编译能力是利用根因;该能力在受影响的 H2 版本上默认可用,无需额外配置。
  • JDK:标注 All JDk——仅使用 Runtime.getRuntime().exec,不触碰高版本 JDK 收紧的模块/反射限制,故不挑 JDK 版本(对比字节码加载类 H2 gadget 常受 JDK 版本约束)。
  • 触发条件:应用侧存在「用可控 JDBC URL 建连」的注入点(如可控数据源配置、Fastjson/Jackson 反序列化触达 getConnection、C3P0/HikariCP/DBCP 等连接池的 JDBC URL 可控)。仅建立连接即触发,无需应用执行业务 SQL。
  • 坑点cmd 未转义,特殊字符会破坏 payload;MODE=MSSQLServerBEFORE SELECT ON INFORMATION_SCHEMA.TABLES 的组合是为保证建连即触发,改动这两处可能导致触发器不被调用。

所属预设链

  • usedInChains 为空:自由构件,未直接出现在 51 条预设链中。可按标签自行组合到任意「JDBC URL 注入」型入口之后(承接 JdbcUrlChains / H2JdbcUrl 的 Payload 或上游 gadget)。

关联

  • 同族 H2 JDBC URL sink(同为 H2JdbcUrl/JdbcUrlChains/END):
    • H2JavaExecJdbc1CREATE ALIAS + CALL 变体,命令执行。
    • H2JavaJdbc1 / H2JavaJdbc2 / H2JavaJdbc3:加载任意字节码(走 javac/Base64 解码),能力更强但 payload 更大、部分受 JDK 版本约束。
    • H2GroovyExecJdbc / H2JsExecJdbc / H2JavaReverseJdbc / H2RemoteJdbc:Groovy/JS 执行、反弹 shell、远程库等其它 H2 变体。
  • 上游承接者(tags=JdbcUrlChains):JDBC URL 注入类 Payload 入口,以及各连接池/数据源工厂 JdbcAttack(如 HikariJdbcAttackDruidJdbcAttackTomcatDbcp2JdbcAttack 等,见 )。

防御与检测

  • 检测特征(流量 / 配置 / 日志)
    • JDBC 连接串中出现 jdbc:h2: 且携带 init= / INIT=(大小写不敏感)。
    • URL 中包含 CREATE TRIGGER / CREATE ALIAS$$(内联源码块定界符)、INFORMATION_SCHEMA.TABLESRuntime.getRuntime().execRUNSCRIPTMODE=MSSQLServer 等关键片段。
    • H2 在建连时编译内联 Java 会在类路径/临时目录留下动态编译痕迹,进程树中 H2 宿主进程 fork 出 cmd.exe/calc.exe/sh 等异常子进程。
  • 加固建议
    • 禁止 JDBC URL 由外部输入直接拼装/覆盖;对数据源 URL 做白名单(仅允许固定 host/driver/参数),拒绝 init/INIT/RUNSCRIPT/TRIGGER/ALIAS 等危险参数与关键字。
    • 生产环境移除不必要的 H2 驱动依赖;若必须使用 H2,禁用其允许在连接参数中执行初始化 SQL 的能力,升级到修复相关行为的 H2 版本。
    • 收敛反序列化面(Fastjson/Jackson autoType、连接池反序列化),阻断「可控对象 → 可控 JDBC URL → getConnection」的链路。
    • 对应用运行账户做最小权限与出网/子进程限制,降低命令执行后果。