导语:PostgreSQL 类型混淆漏洞 CVE-2026-120148 以 CVSS 8.1 的高危评级击穿了数据库内核中一条长期被忽视的信任边界——扩展统计表达式中的匿名 RECORD 类型,其后端本地类型标识符在跨会话场景下可能指向另一会话中的不同行类型,最终让一个仅有对象创建权限的低权用户获得数据库运行用户的操作系统级代码执行能力。本文以架构师视角完整拆解这条攻击链,并给出可直接落地的生产加固方案。
一、PostgreSQL 类型混淆漏洞的安全画像与影响面
CVE-2026-120148 被评定为 High 级别,CVSS 3.x 基础评分 8.1,攻击向量为网络可达、低权限认证、无需用户交互。它的核心问题不是传统意义上的缓冲区溢出,而是 PostgreSQL 类型系统在“扩展统计(Extended Statistics)”这一相对冷门的子系统中,对后端本地类型标识符(backend-local type OID)的语义假设出现了偏差。
在 PostgreSQL 的进程模型中,每个客户端连接对应一个独立的后端进程,类型 OID 在单个后端内是稳定的,但跨会话并不构成全局唯一语义。当扩展统计表达式引用一个匿名 RECORD 类型时,该类型的后端本地标识符可能被错误地解释为另一会话中某个完全不同的行类型,从而触发类型混淆(Type Confusion)。攻击者一旦能够控制这一错配,就可以构造出内核在错误类型假设下执行的内存布局,进而以数据库运行用户(通常是 postgres)的身份执行任意代码。
该漏洞的准入门槛极低:仅需对象创建权限(CREATE 权限),无需超级用户。在多租户数据库、SaaS 平台或允许业务方自助建表的场景中,这意味着一个普通业务账号即可成为攻击起点,横向升级为操作系统级 RCE。
1.1 受影响版本矩阵
| 大版本 | 受影响版本 | 修复版本 | 处置优先级 |
|---|---|---|---|
| PostgreSQL 18 | < 18.7 | 18.7 | 立即升级 |
| PostgreSQL 17 | < 17.12 | 17.12 | 立即升级 |
| PostgreSQL 16 | < 16.16 | 16.16 | 立即升级 |
| PostgreSQL 15 | < 15.20 | 15.20 | 立即升级 |
| PostgreSQL 14 | < 14.25 | 14.25 | 立即升级 |
企业级架构规范
flowchart TD
subgraph 攻击面 ["攻击面与接入层 (Attack Surface Tier)"]
A["低权业务账号 (仅 CREATE 权限)"] --> B["创建扩展统计对象 (CREATE STATISTICS)"]
B --> C["构造匿名 RECORD 类型统计表达式"]
end
subgraph 混淆层 ["类型系统与信任边界 (Type Confusion Tier)"]
C --> D["后端本地类型标识符 (backend-local OID)"]
D --> E["跨会话行类型错配 (Type Confusion)"]
end
subgraph 执行层 ["内核执行与提权 (Kernel Execution Tier)"]
E --> F["错误类型假设下的内存布局"]
F --> G["以 postgres 用户执行任意代码 (RCE)"]
end
subgraph 防御层 ["纵深防御与加固 (Defense Tier)"]
G -.-> H["升级至 18.7 / 17.12 / 16.16 / 15.20 / 14.25"]
H --> I["权限收敛与扩展统计审计"]
I --> J["数据库进程最小权限运行"]
end
二、原理拆解:PostgreSQL 类型混淆漏洞为何能跨越会话边界
要理解这个 PostgreSQL 类型混淆漏洞,必须先回到 PostgreSQL 的进程架构。PostgreSQL 采用多进程模型,每个连接由独立的 backend 进程服务,类型 OID 在 pg_type 中全局分配,但匿名 RECORD 类型是一个特殊存在:它没有固定的 pg_type 条目,其类型标识符由后端在运行时动态分配,仅在本后端内有效。
2.1 扩展统计表达式的执行路径
扩展统计允许用户对多列相关性、函数依赖等进行统计建模,其表达式在 CREATE STATISTICS 时被解析并存储,在 ANALYZE 或查询规划阶段被重新加载执行。问题出在重新加载环节:内核从存储中恢复表达式时,若其中包含匿名 RECORD 类型,会依据后端本地标识符去解析对应的行类型描述符(TupleDesc)。
- 会话 A 创建扩展统计,表达式中匿名 RECORD 类型被分配本地标识符 X,指向行类型 R1。
- 会话 B 中本地标识符 X 恰好被另一行类型 R2 占用,且 R2 的字段布局与 R1 不同。
- 内核在会话 B 中执行该统计表达式时,按 R2 的布局解释本应按 R1 处理的内存,形成类型混淆。
- 攻击者精心构造 R2 的字段类型(如变长字段、指针字段),使内核在错误布局下产生可控的内存读写。
这类漏洞的本质是“本地标识符被当作全局标识符使用”的信任边界错误。在分布式或多进程系统中,任何依赖进程本地 ID 跨边界传递的设计,都必须显式携带类型/版本校验,否则类型混淆几乎是必然结果。
2.2 从类型混淆到任意代码执行
类型混淆之所以危险,是因为它把“类型安全”这一内存安全基石抽走了。内核在错误类型假设下,可能将整数解释为指针、将短结构解释为长结构,从而产生越界读写。攻击者结合 PostgreSQL 的扩展机制与函数调用路径,可将可控内存破坏转化为以 postgres 用户身份执行任意代码。由于数据库进程通常拥有数据目录的读写权限,RCE 之后可进一步读取全库数据、植入持久化后门,甚至横向渗透宿主机。
很多团队会误判“只有超级用户才能创建统计对象”。实际上 CREATE STATISTICS 仅要求对目标表拥有属主或相应权限,在多租户或自助建表平台中,普通业务账号完全可能具备该能力。请立即审计 pg_statistic_ext 中的对象属主分布。
三、生产应急响应与加固落地清单
面对 CVSS 8.1 的 PostgreSQL 类型混淆漏洞,正确的响应顺序是:先止血、再修复、后审计。以下清单可直接作为 DBA 与安全团队的联合行动手册。
3.1 第一步:版本升级(根本修复)
官方已在 18.7、17.12、16.16、15.20、14.25 中修复该问题。小版本升级通常无需数据迁移,但生产环境仍建议遵循“备份—灰度—验证”的标准流程。
点击展开:Debian/Ubuntu 与 RHEL 系升级命令及回滚预案
# Debian / Ubuntu (PGDG 源) sudo apt-get update sudo apt-get install --only-upgrade postgresql-16 sudo systemctl restart postgresql # RHEL / Rocky / AlmaLinux sudo dnf update postgresql16-server postgresql16-libs sudo systemctl restart postgresql-16 # 升级后校验版本 psql -c "SELECT version();" # 回滚预案:保留旧二进制与数据目录快照 # 若使用容器,固定镜像 tag 并保留上一版本镜像
3.2 第二步:权限收敛与对象审计
在无法立即升级的窗口期,权限收敛是最有效的缓解手段。核心思路是:收回普通业务账号的 CREATE 权限,并审计存量扩展统计对象。
-- 1. 审计所有扩展统计对象及其属主
SELECT s.stxname, s.stxnamespace::regnamespace AS schema,
r.rolname AS owner, s.stxexprs IS NOT NULL AS has_exprs
FROM pg_statistic_ext s
JOIN pg_roles r ON r.oid = s.stxowner
ORDER BY owner, schema;
-- 2. 收回业务 schema 的公共建表权限
REVOKE CREATE ON SCHEMA app_schema FROM PUBLIC;
-- 3. 对确有建表需求的账号,改为通过受控角色授权
CREATE ROLE app_ddl NOLOGIN;
GRANT CREATE ON SCHEMA app_schema TO app_ddl;
-- 仅授予可信运维账号
GRANT app_ddl TO ops_admin;
-- 4. 检查是否存在匿名 RECORD 类型的统计表达式
SELECT stxname FROM pg_statistic_ext
WHERE stxexprs::text LIKE '%record%';
若审计发现来源不明的扩展统计对象,尤其是属主为低权业务账号且表达式含 RECORD 类型的对象,应视为潜在失陷指标,立即隔离并排查数据库日志中的异常连接与 ANALYZE 调用。
3.3 第三步:进程最小权限与纵深防御
即便完成升级,也建议把“数据库进程被攻陷”纳入威胁模型。具体措施包括:以专用低权系统用户运行 PostgreSQL、限制数据目录权限、启用 log_connections 与 log_statement 审计、对数据库主机实施网络微隔离,避免 RCE 后直接横向移动。
| 加固维度 | 传统做法 | 现代加固方案 | 架构收益 |
|---|---|---|---|
| 版本管理 | 长期不升级小版本 | 订阅安全通告 + 季度小版本升级 | 漏洞窗口缩短 90% |
| 权限模型 | 业务账号直接 CREATE | DDL 角色隔离 + 按需授权 | 攻击面收敛 |
| 进程权限 | postgres 用户高权限运行 | 专用低权用户 + 目录最小权限 | RCE 影响降级 |
| 审计监控 | 仅记录错误日志 | 连接/语句全审计 + 异常告警 | 失陷可发现 |
四、架构师复盘:从 PostgreSQL 类型混淆漏洞看信任边界设计
这起 PostgreSQL 类型混淆漏洞给所有系统架构师上了一堂生动的信任边界课。类型系统、ID 分配、跨会话状态传递,这些看似底层的内核细节,一旦假设被打破,就会从“理论风险”迅速演变为“可远程利用的高危漏洞”。
- 本地标识符不可跨边界裸传:任何 backend-local、thread-local、request-local 的 ID,在跨进程/跨会话传递时必须附带类型标签与版本校验。
- 冷门子系统往往是安全洼地:扩展统计、物化视图、自定义聚合等低频特性,代码审查与模糊测试覆盖通常弱于主查询路径,应纳入重点安全测试范围。
- 权限即攻击面:CREATE 权限在多数团队眼中是“无害”的,但在数据库内核漏洞面前,它就是 RCE 的入场券。最小权限原则必须落实到 DDL 粒度。
- 应急响应要可执行:安全通告的价值不在于“知道”,而在于“能在一小时内完成升级、审计与加固”。把本文的 SQL 清单沉淀为 Runbook,才是真正的工程化落地。
建议将 PostgreSQL 小版本升级纳入 CI/CD 流水线的依赖治理范畴,像管理应用依赖一样管理数据库内核版本,并订阅官方安全通告源,形成“通告—评估—灰度—验证”的闭环。
本文核心技术背景源自 GitHub Security Advisory 官方安全通告:
GHSA-xjmm-59mp-3mvc / CVE-2026-120148 (PostgreSQL 扩展统计类型混淆)
