Skip to content

Latest commit

 

History

History
121 lines (93 loc) · 11.3 KB

File metadata and controls

121 lines (93 loc) · 11.3 KB

DruidDataSource JDBC URL Attack(DruidDataSource2)

  • 类别:javanative 别名:— 优先级:—
  • 作者:—
  • 依赖com.alibaba:druid:1.2.16

作用

把内层「JDBC URL」子链的产物包装进一个 com.alibaba.druid.pool.DruidDataSource 连接池对象。当上游的 getter 触发型 gadget(Fastjson / Jackson / Rome 等)在反序列化时调用该对象的 getConnection()(对应属性名 connection)时,Druid 连接池会用预置的 url 真正建立 JDBC 连接,从而把控制权交给内层的恶意 JDBC URL(H2 RCE、MySQL 任意文件读取、Derby 命令执行等)。

本 gadget 本身不是字节码 sink,也不是反序列化入口,而是「getter 段」与「JDBC URL 段」之间的桥接/适配环:对上通过 getConnection getter 被触发,对下消费一段 JDBC URL 利用链。

链接标签

  • 入口 tags
    • DataSourceChains —— 声明自己属于「DataSource 触发」这一类,供上游 getter 型 gadget(nextTagsDataSourceChains 者,如 FastjsonJackson、Rome 系列、DWrap)衔接。
    • Getter —— 表明本环的触发方式是「被调用 getter」,即靠 getConnection() 这个 getter 被反射/JSON 反序列化调用来启动。
    • NotForJavaSerializable —— 关键约束DruidDataSource 无法安全地走 Java 原生序列化(其内部字段包含不可序列化状态),因此本环只适用于「对象被现场 new 出来再调用 getter」的场景(Fastjson/Jackson 等 JSON 反序列化、或经 DWrap 动态代理包裹),不能直接嵌入纯 Java readObject 链。对比同目录的 DruidDataSource(用 DruidXADataSource)未标此 tag。
    • END —— 在「getter 段」视角下,本环是该段的收口:getter 被触发后即进入 Druid 内部逻辑,无需再往外包一层 getter 载体。注意 ENDnextTags 并存并不矛盾——END 指它是 getter 触发链的终点,nextTags 指它仍要向内接一段 JDBC URL 子链。
  • 衔接 nextTags
    • JdbcUrlChains —— 之后(内层)接一个「只需一个 JDBC URL 字符串」的利用链(如 H2JavaJdbc1MysqlJdbcPostgreSqlJdbcSqliteJdbc 等)。
    • JdbcUrlWithSQLChains —— 之后接一个「除 URL 外还要附带初始化 SQL 列表」的利用链(如 DerbyCommandJdbcHsqldb*Jdbc 系列);这些 SQL 由 Druid 的 connectionInitSqls 在建连后自动执行。
  • excludes:无。

源码剖析

本类直接 implements Gadget,无基类。核心是两段:getObject(构造 Druid 连接池对象)与 invoke(取内层 JDBC URL、写入 context、并调用 getObject)。

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

public Object getObject(String jdbcUrl, List<String> sqlList) throws Exception {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setLogWriter(null);
    dataSource.setStatLogger(null);
    dataSource.setUrl(jdbcUrl);                                             // ① 恶意 JDBC URL
    dataSource.setInitialSize(1);                                          // ② 初始连接数 1,触发一次建连即可
    Reflections.setFieldValue(dataSource, "transactionHistogram", null);   // ③ 反射清空干扰字段
    Reflections.setFieldValue(dataSource, "statLogger", null);
    Reflections.setFieldValue(dataSource, "initedLatch", null);
    if (sqlList != null) {
        Reflections.setFieldValue(dataSource, "connectionInitSqls", sqlList); // ④ 建连后自动执行的 SQL
    }
    return dataSource;
}

逐段说明:

  • setUrl(jdbcUrl):把内层子链产出的恶意 JDBC URL 塞进连接池。真正的利用发生在建连时——Druid 会按 URL 前缀选择/加载 JDBC 驱动并连接,H2/Derby/MySQL 等驱动各自的「URL 特性」即是漏洞面(H2 的 INIT=RUNSCRIPT、MySQL 的 allowLoadLocalInfile 读文件、Derby 的 create=true 加载类等)。
  • setInitialSize(1) + setLogWriter(null) / setStatLogger(null):只需最小化配置让连接池能被初始化并触发一次建连;关闭日志/统计避免噪声与额外依赖。
  • ③ 反射置空 transactionHistogram / statLogger / initedLatch:这些是 Druid 内部的监控直方图、统计日志器与初始化闭锁字段。它们默认引用了在攻击者环境中不存在或会抛异常的对象;置空后可让对象在受害端顺利完成 getConnection 内部的初始化流程(init())而不因这些辅助字段报错。这也是本环被标 NotForJavaSerializable 的根因——对象状态是手工「削」出来的,不具备可序列化完整性。
  • connectionInitSqls = sqlList:Druid 支持「连接初始化 SQL」,在每次物理建连后立即执行列表中的 SQL。这正是 JdbcUrlWithSQLChains(Derby/Hsqldb 命令执行类)依赖的机制:URL 负责挂载/加载,SQL 负责触发存储过程或 RCE。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    Object jdbcObj = chain.doCreate(context);                               // 取内层 JDBC URL 子链产物
    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");            // 告知上游:getter 属性名
    String jdbcUrl = "";
    List<String> sqlList = new ArrayList<String>();
    if (jdbcObj instanceof String) {                                        // 情形 A:内层直接给 URL 字符串
        jdbcUrl = (String)jdbcObj;
    } else if (jdbcObj instanceof Map) {                                    // 情形 B:内层给 URL + SQL 列表
        Map jdbcMap = (Map)jdbcObj;
        jdbcUrl = (String)jdbcMap.get(ContextTag.JDBC_URL);
        sqlList = (List)jdbcMap.get(ContextTag.JDBC_URL_SQL_LIST);
    }
    return this.getObject(jdbcUrl, sqlList);
}

invoke 体现了链「由内向外」构建的原则:

  1. chain.doCreate(context) 先让内层(JDBC URL 段)产出结果。约定两种形态:单纯 String(对应 JdbcUrlChains),或携带 JDBC_URLJDBC_URL_SQL_LIST 两键的 Map(对应 JdbcUrlWithSQLChains)。
  2. 三个 context.put 是与上游 getter 型 gadget 的「握手」:把「要触发的父类型 = DataSource、方法 = getConnection、属性名 = connection」写入共享 GadgetContext。上游 getter 载体(如 Fastjson/Jackson 的属性求值、或 CommonsBeanutils 家族通过 GETTER_PARAM_NAME_KEY 读取属性名)据此在反序列化时精确地调用 getConnection(),而不是盲目遍历所有 getter。
  3. 最后调用 getObject 返回构造好的 DruidDataSource 实例,交给上游继续外包。

触发时序(反序列化端,自外向内执行):上游 getter gadget 触发 DruidDataSource.getConnection() → Druid init()url 建立物理连接 → 加载对应 JDBC 驱动、解析 URL 恶意特性 →(若有)执行 connectionInitSqls → 完成 RCE / 文件读取 / 类加载。

参数(@Param)

本类无 @Param。可配内容全部来自内层 JDBC URL 子链jdbcUrlsqlListJdbcUrlChains / JdbcUrlWithSQLChains 段的 gadget 提供,本环仅做转发与包装。

适用版本与原理要点

  • 依赖 com.alibaba:druid:1.2.16(源码基于该版反编译)。原理依赖的是 Druid 通用的「连接池初始化即建连」与 connectionInitSqls 行为,跨小版本大体通用;反射置空的内部字段名(transactionHistogram/statLogger/initedLatch/connectionInitSqls)与具体版本的私有字段实现绑定,换版本需核对字段是否存在。
  • 只走 getter 触发链NotForJavaSerializable 决定它不能进纯 Java 原生序列化链,典型入口是 Fastjson/Jackson 这类「对 JSON 反序列化目标对象逐属性调用 getter」的场景;也可经 DWrap 用 Spring JdkDynamicAopProxy 包裹以规避 Jackson 直接调用 DataSource 的不稳定问题。
  • 实际杀伤来自内层 URL/SQL:本环不产出字节码,最终效果取决于所选 JDBC 方言 gadget——H2(内存库 RUNSCRIPT/别名函数 RCE)、Derby(connectionInitSqls 触发命令/类加载)、MySQL(恶意服务端读文件/反序列化)、Postgres/Sqlite 等。
  • DruidDataSource 的区别:后者用 com.alibaba.druid.pool.xa.DruidXADataSource(XA 数据源),逻辑与字段置空完全一致,但未标 NotForJavaSerializable;两者可视为同一利用的「普通池 / XA 池」双变体,按目标环境可加载哪个类择一。

所属预设链

usedInChains 为空——自由构件,未被 default-chains.yaml 中的 51 条预设链直接固定引用。但它是「Druid Jdbc」类利用的核心桥接环,可按 tag/nextTag 自由拼装,典型形态:

[<getter 载体: Fastjson/Jackson/...>, DruidDataSource2, <JDBC URL 段: H2JavaJdbc1 / MysqlJdbc / DerbyCommandJdbc / ...>]

(参考同族的 H2JavaExecJdbc1 曾出现在名为「Druid Jdbc」的示例链中。)

关联

防御与检测

  • 检测特征
    • 反序列化流量/内存中出现 com.alibaba.druid.pool.DruidDataSource(或 ...xa.DruidXADataSource)实例,且其 url 指向可疑方言(jdbc:h2:memjdbc:derby:jdbc:mysql://allowLoadLocalInfile/autoDeserializejdbc:postgresql://...socketFactory 等)。
    • Fastjson/Jackson 报文中出现 dataSource/connection 相关属性并伴随 JDBC URL 字符串。
    • 运行期出现「反序列化线程发起对外 JDBC 连接」的异常调用栈(DruidDataSource.getConnectioninit)。
  • 加固建议
    • 禁用/收敛 Fastjson autoType、Jackson 默认多态类型(enableDefaultTyping),对 DataSource/DruidDataSource 等类型加入反序列化黑名单。
    • JDBC 驱动侧禁危险 URL 参数:MySQL 关闭 allowLoadLocalInfileautoDeserialize;避免在服务端引入 H2/Derby 等可被 URL 直接触发代码执行的驱动。
    • 对连接池建连做出网/目的地白名单,阻断反序列化上下文中意外发起的 JDBC 连接。
    • 升级并最小化依赖,移除生产环境不需要的 com.alibaba:druid 与嵌入式数据库驱动。