Skip to content

Latest commit

 

History

History
67 lines (56 loc) · 4.67 KB

File metadata and controls

67 lines (56 loc) · 4.67 KB

PGSimpleDataSource PostgreSQL JDBC URL Attack(PostgreSqlDataSource2)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖org.postgresql:postgresql

作用

把一个恶意 PostgreSQL JDBC URL 装进 org.postgresql.ds.PGSimpleDataSource,当上游 gadget 触发其 getConnection()(一个 getter)时,PostgreSQL 驱动会向该 URL 发起连接,从而进入 PostgreSQL JDBC 利用链(CVE-2022-21724 等)。本类是「getter 触发 → JDBC URL sink」之间的中间跳板。

链接标签

  • 入口 tagsDataSourceChainsGetter —— 既是数据源类跳板,又能承接上一环「调用任意 getter」的 gadget(如 CommonsBeanutils1 或各类 EqualsBean/ToStringBean),由上游调用本对象的 getConnection
  • 衔接 nextTagsPostgreSQLJdbcUrl —— 之后需接一个产出「PostgreSQL 恶意 JDBC URL 字符串」的 gadget(如 PostgreSqlJdbc,标签正是 PostgreSQLJdbcUrl)。
  • excludes:无。

源码剖析

private final String getterMethodName = "getConnection";
private final String getterPropertyName = "connection";

public Object getObject(String jdbcUrl) throws Exception {
    try {
        CtClass ctClass1 = ClassPool.getDefault().get("org.postgresql.ds.common.BaseDataSource");
        CtMethod writeReplace1 = ctClass1.getDeclaredMethod("getURL");
        ctClass1.removeMethod(writeReplace1);
        ctClass1.toClass();
    } catch (Exception ctClass1) {
        // empty catch block
    }
    PGSimpleDataSource dataSource = new PGSimpleDataSource();
    dataSource.setURL(jdbcUrl);
    return dataSource;
}

getObject 先用 Javassist 从 PGSimpleDataSource 的父类 org.postgresql.ds.common.BaseDataSource删除 getURL() 方法toClass() 重新载入。原因:新版 BaseDataSource.getURL() 会依据 host/port/database 等属性重新拼装 URL,忽略直接 setURL 的原值;删掉 getURL 后,getConnection 内部读取 URL 时回落到用 setURL 存下的原始字段,保证攻击者构造的完整恶意 URL 原样生效。随后 new PGSimpleDataSource()setURL(jdbcUrl),返回这个数据源对象。

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    String jdbcUrl = (String)chain.doCreate(context);
    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(jdbcUrl);
}

invoke 先由 chain.doCreate(context) 取得内层对象——即下游 PostgreSQLJdbcUrl gadget 产出的恶意 JDBC URL 字符串。随后向 GadgetContext 注入三条元信息,供上游 getter 触发器读取:

  • SUPER_CLASS_NAME_KEY = DataSource.class:告诉上游包装对象的目标类型是 javax.sql.DataSource
  • SPECIAL_METHOD_NAME_KEY = "getConnection":上游需要调用的方法名。
  • GETTER_PARAM_NAME_KEY = "connection":对应的属性名,供 CommonsBeanutils1 这类基于 property 的比较器(PropertyUtils.getProperty(obj,"connection")getConnection())反射调用。

因此反序列化触发时,路径为:上游 getter 触发器 → PGSimpleDataSource.getConnection() → PostgreSQL 驱动解析恶意 URL → 进入 JDBC sink。

参数(@Param)

本类无 @Param。恶意 URL 由下游 PostgreSQLJdbcUrl gadget(如 PostgreSqlJdbc)的参数决定;getterMethodName/getterPropertyName 为写死的私有常量。

适用版本与原理要点

  • 依赖 org.postgresql:postgresql(PGSimpleDataSource 位于该驱动内)。实际可利用范围由下游 JDBC gadget 决定,如 PostgreSqlJdbc 声明 9.4.1208 <= postgresql < 42.2.2542.3.0 <= postgresql < 42.3.2(CVE-2022-21724)。
  • 关键坑点:必须删除 BaseDataSource.getURL() 才能让 setURL 的完整恶意 URL 不被属性拼装覆盖;该动作依赖 Javassist(javassist.ClassPool)在运行时改写并重载类。
  • 触发依赖上游能调用任意 getter(getConnection),故常与 CB/Rome 等「Getter」触发器组合。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。

关联

  • 上游(承接 Getter):CommonsBeanutils1 及各类 EqualsBean/ToStringBean getter 触发器。
  • 下游(nextTags=PostgreSQLJdbcUrl):PostgreSqlJdbc(CVE-2022-21724,产出 spring-bean.xml 加载 URL)、PostgreSqlJdbc4Jndi
  • 同族:PostgreSqlDataSource(同为 PGSimpleDataSource 跳板)、MysqlDataSourceDataSourceChains 系列。