PostgreSQL 18.6 - 高危 - 远程 - 文件写入 - pg_basebackup 跟随绝对路径 tar 符号链接,CVE-2026-6475 遗留问题
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · postgres-basebackup-symlink
类别: 文件写入 可达性: 远程
PostgreSQL 18.6 (724edf9) - PostgreSQL Global Development Group
CVE-2026-6475 为 tar 成员名称添加了 path_is_safe_for_extraction。绝对 符号链接目标仍然跳过该检查,以便表空间正常工作。随后 astreamer_file.c 对目标执行 symlink(),之后 fopen(..., "wb") 会跟随该链接。默认的 pg_basebackup 格式为 plain(-F p)。pg_rewind 的 create_target_symlink 会检查路径且从不创建链接。
一个能说 BASE_BACKUP 的恶意源可以植入绝对符号链接,并覆盖备份 UID 可写入的任何文件。 与 6475 相同的受害者模型。这不是成员名称检查的重复提交。
| ID | 暂无 CVE |
| 类别 | 文件写入(CVE-2026-6475 遗留的绝对 tar 符号链接) |
| 可达性 | 远程(UI:R 客户端 vs 恶意 PostgreSQL) |
| CWE | CWE-59, CWE-22 |
| CVSS | High: 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| 产品 | PostgreSQL |
| 受影响版本 | 18.6 标签 REL_18_6 (724edf9bde9d356724ad384a2e196edc3c9f80f7),源码树内 pg_basebackup / pg_rewind |
| 认证 | 受害者针对攻击者源运行默认的 pg_basebackup -D <dir> |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1 |
能让操作员针对其 PostgreSQL 运行 pg_basebackup -D 的人(与 CVE-2026-6475 相同的 UI:R 模型)可以写入 -D 之外的位置。
| 权限 | 方法 / 汇聚点 | 实验验证或推断 |
|---|---|---|
完整性:在 -D 之外写入文件 | 恶意 BASE_BACKUP tar:typeflag '2' 成员,具有安全的相对名称(pg_tblspc/16384)和绝对链接目标,然后在该名称下放置一个常规成员。extract_link 之后 create_file_for_extract 执行 fopen(..., "wb"),没有 O_NOFOLLOW | 实验验证。在 /work/oracle/ 写入了 POSTGRES-BASEBACKUP-SYMLINK-WITNESS。目标位置保留了该符号链接。对照 tar(无符号链接)使 oracle 保持为空,且仅在 -D 内写入 PG_VERSION |
完整性:覆盖 shell rc、authorized_keys、systemd 用户单元或 crontab | 同一汇聚点,客户端 UID 可写入的任何路径 | 推断。本实验不触碰这些文件 |
| 可用性:破坏客户端配置或密钥 | 同一写入 | 推断。未演示 |
| 客户端磁盘侦察 | 提取是写入,不是读取 | 已排除 |
遍历 .. 或绝对 成员名称 | path_is_safe_for_extraction | 已排除。那是 CVE-2026-6475 |
| 服务端非特权 SQL / 未认证协议 | 18.6 默认服务器 | 已排除。此遗留问题在客户端 |
| 仅 tar 备份 | pg_basebackup -F t 存储 tar 而不提取 | 已排除 |
在 postgres 内 RCE | 不是此汇聚点 | 已排除 |
pg_rewind 有相同的 create_target_symlink / open() 遗留问题。本实验仅驱动 pg_basebackup。
在 18.6 浪潮之后(28 个 CVE,加上此源码树上已有的 18.4/18.2 集合),我锁定了 postgres/postgres REL_18_6。二十二轮搜寻。默认服务器、非特权 SQL、未认证协议:在此标准下均无发现。CVE-2026-6475 是需要重新审视的同类问题。
成员名称被检查。绝对符号链接目标未被检查:
if (!is_absolute_path(linktarget) &&
!path_is_safe_for_extraction(member->linktarget))
{
pg_fatal("link target has unsafe path name: \"%s\"",
member->linktarget);
}
extract_link(mystreamer->filename, linktarget);
create_file_for_extract 仍然是 fopen(filename, "wb")。astreamer_file.c:351-356 处的注释说映射被应用,但没有检查链接是否位于 pg_tblspc 内。
已记录的弯路:将 6475 视为已关闭而离开;将 -D 指向活动数据目录;为作秀而覆盖 shell rc。一个诚实的 18.6 服务器只会发出 pg_tblspc/<oid> 符号链接,并将表空间内容放在单独的归档中。一个能说 BASE_BACKUP 的恶意源可以将后续的常规成员放在同一个 tar 中。这就是遗留问题。
python3 postgres-basebackup-symlink-Abraxas-Labs.py
或
cd lab
./run.sh
postgres:18.6 作为源镜像,运行 BASE_BACKUP 说话者。客户端是来自同一标签的源码树内 pg_basebackup(pg_basebackup (PostgreSQL) 18.6)。回环 127.0.0.1:19370。Compose 项目 postgres-basebackup-symlink。不要将 -D 指向活动数据目录。
SUCCESS postgres-basebackup-symlink control-inside=yes oracle-outside=yes dest-is-symlink=yes image=postgres:18.6 how=pg_basebackup POSTGRES-BASEBACKUP-SYMLINK-WITNESS
将绝对符号链接目标视为不安全,除非它们被 --tablespace-mapping 重写。使用 O_NOFOLLOW / lstat 打开提取文件,使植入的链接无法被跟随。将 path_is_safe_for_extraction 应用于 rewind 的 link 以及 path。拒绝路径穿过离开 -D 的符号链接的常规成员。
724edf9bde9d356724ad384a2e196edc3c9f80f7)src/fe_utils/astreamer_file.c · src/fe_utils/astreamer_tar.csrc/bin/pg_rewind/file_ops.cGNU Affero GPL v3.0。参见 LICENSE。仅限回环实验。无担保。