导语:PostgreSQL plperl UAF 漏洞(CVE-2026-116307)以 CVSS 8.8 的高危评级进入 2026 年 11 月安全通告,其核心风险在于:一个仅具备对象创建权限的低权用户,可通过 plperl 解释器重入触发 use-after-free,最终以数据库进程的 OS 用户身份执行任意代码。本文以架构师视角完整拆解该漏洞的触发时序、影响面与工业级应急加固方案。
一、PostgreSQL plperl UAF 漏洞全景:从 CVSS 8.8 到真实攻击面
CVE-2026-116307 被归类为 plperl 扩展中的 use-after-free(释放后使用)缺陷。官方描述非常克制但信息量极大:“对象创建者可通过重入 plperl,在外层调用仍在执行某个预编译查询时将其释放,从而以运行数据库的操作系统用户身份执行任意代码。”这句话里包含了三个关键要素——攻击者身份、触发机制、最终影响,我们逐一拆解。
- 攻击者身份:并非超级用户,而是“对象创建者”(object creator),即拥有 CREATE 权限、能在自有 schema 中创建函数/过程的普通角色。这意味着在多租户或共享数据库场景中,威胁模型被显著放大。
- 触发机制:plperl 解释器重入(re-entering plperl)+ 预编译查询(prepared query)在外层调用执行期间被释放,形成经典的 UAF 时序窗口。
- 最终影响:以数据库服务进程的 OS 用户(通常是
postgres)执行任意代码,CVSS 向量为AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H,机密性、完整性、可用性全部为 High。
该漏洞的 CVSS 评级为 8.8(High),但 Ubuntu 安全通告将其标注为 medium,二者存在评级差异。从架构防御角度,应以 CVSS 8.8 为准:低权限前置条件 + 网络可达 + 无需用户交互 + 完整 RCE 能力,是典型的“低门槛高收益”攻击链。
企业级架构规范
flowchart TD
subgraph 接入层 ["表现层与接入网关 (Client & Gateway Tier)"]
A["低权角色连接 (CREATE 权限)"] --> B["SQL 调用 plperl 函数"]
end
subgraph 规则层 ["权限校验与上下文层 (Policy & Context Tier)"]
B --> C["plperl 解释器初始化"]
C --> D["外层调用持有预编译查询句柄"]
end
subgraph 领域层 ["核心执行与重入引擎 (Core Reentry Tier)"]
D --> E["函数体内重入 plperl"]
E --> F["内层释放预编译查询内存"]
F --> G["外层继续使用已释放指针 (UAF)"]
end
subgraph 存储层 ["基础设施与 OS 执行层 (Infrastructure Tier)"]
G --> H["堆内存布局操控"]
H --> I[("以 postgres OS 用户执行任意代码")]
end
二、原理深挖:plperl 解释器重入与预编译查询释放时序
2.1 plperl 的执行模型与 SPI 预编译查询
plperl 是 PostgreSQL 的可信 Perl 过程语言扩展,允许在数据库内以 Perl 编写函数。其执行模型的关键在于:每次调用 plperl 函数时,PostgreSQL 会通过 SPI(Server Programming Interface)准备并执行 SQL 语句。为了性能,SPI 支持“预编译查询”(prepared query)——查询计划被缓存并复用,对应的内存上下文(MemoryContext)在多次调用间保持存活。
当 plperl 函数内部再次调用另一个 plperl 函数(即“重入”),解释器栈会嵌套。此时,外层调用持有的预编译查询句柄仍指向同一块内存上下文。如果内层调用路径上触发了该查询的释放(例如通过某些 SPI 管理操作或错误处理路径),外层调用返回后继续访问该句柄,就形成了 use-after-free。
2.2 UAF 窗口的形成条件
- 条件一:攻击者能够创建 plperl 函数(需要 plperl 扩展已安装且角色具备 CREATE 权限)。
- 条件二:函数体内构造重入调用,使外层与内层共享同一预编译查询的引用。
- 条件三:内层路径触发释放,外层返回后继续解引用,形成悬垂指针。
- 条件四:攻击者通过堆喷射(heap grooming)控制释放后的内存内容,将函数指针或数据结构劫持到可控地址。
UAF 漏洞的可怕之处不在于“释放”本身,而在于“释放后内存被重新分配并可控”。PostgreSQL 的 MemoryContext 机制虽然提供了较强的内存隔离,但预编译查询句柄跨上下文复用的设计,为攻击者提供了稳定的利用原语。修复版本的核心改动正是加强了重入场景下的引用计数与生命周期管理。
三、影响范围与版本矩阵:谁在射程之内
根据官方通告,所有低于以下版本的分支均受影响:PostgreSQL 18.7、17.12、16.16、15.20、14.25。Ubuntu 侧,20.04 LTS 的 postgresql-12 包全版本受影响(从 12.0-1 到 12.22-0ubuntu0.20.04.4),这意味着大量仍在运行 Ubuntu 20.04 的生产环境处于暴露状态。
| 分支 | 受影响版本 | 修复版本 | 升级优先级 |
|---|---|---|---|
| PostgreSQL 18 | < 18.7 | 18.7 | P0 立即升级 |
| PostgreSQL 17 | < 17.12 | 17.12 | P0 立即升级 |
| PostgreSQL 16 | < 16.16 | 16.16 | P0 立即升级 |
| PostgreSQL 15 | < 15.20 | 15.20 | P0 立即升级 |
| PostgreSQL 14 | < 14.25 | 14.25 | P0 立即升级 |
| Ubuntu 20.04 postgresql-12 | 全版本(至 12.22-0ubuntu0.20.04.4) | 等待 Ubuntu 安全更新 | P1 评估迁移 |
PostgreSQL 12 已进入 EOL 阶段,Ubuntu 20.04 的 postgresql-12 包虽然仍在维护,但修复节奏可能滞后于上游。对于仍在使用 12.x 的环境,建议将“升级到 14.25+ 或更高受支持分支”纳入季度架构治理计划,而非仅依赖发行版补丁。
四、应急响应与加固实战:从检测到修复的完整路径
4.1 第一步:暴露面检测
在升级之前,先确认环境中是否存在 plperl 扩展以及哪些角色具备创建权限。以下 SQL 可快速完成资产盘点:
-- 1. 检查是否安装了 plperl 扩展
SELECT extname, extversion, extnamespace::regnamespace
FROM pg_extension
WHERE extname IN ('plperl', 'plperlu');
-- 2. 列出所有 plperl 函数及其属主
SELECT n.nspname AS schema_name,
p.proname AS function_name,
pg_get_userbyid(p.proowner) AS owner,
l.lanname AS language
FROM pg_proc p
JOIN pg_language l ON l.oid = p.prolang
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE l.lanname IN ('plperl', 'plperlu')
ORDER BY schema_name, function_name;
-- 3. 检查哪些角色具备 CREATE 权限(潜在攻击者)
SELECT r.rolname, n.nspname, has_schema_privilege(r.rolname, n.nspname, 'CREATE') AS can_create
FROM pg_roles r
CROSS JOIN pg_namespace n
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND has_schema_privilege(r.rolname, n.nspname, 'CREATE')
AND r.rolname NOT LIKE 'pg_%'
ORDER BY r.rolname, n.nspname;
4.2 第二步:版本升级(首选修复)
对于 Debian/Ubuntu 环境,通过官方仓库升级到修复版本是最直接的路径。升级前务必完成逻辑备份与主从切换演练。
点击展开:Ubuntu/Debian 升级执行命令与回滚预案
# 1. 升级前备份(逻辑备份 + 关键配置) sudo -u postgres pg_dumpall -f /backup/pg_all_$(date +%F).sql sudo cp -a /etc/postgresql /backup/etc_postgresql_$(date +%F) # 2. 更新软件源并升级 PostgreSQL 相关包 sudo apt update sudo apt install --only-upgrade postgresql postgresql-contrib # 3. 确认版本 sudo -u postgres psql -c "SELECT version();" # 4. 若使用 PGDG 官方源(推荐获取最新小版本) sudo apt install -y postgresql-common sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh sudo apt update sudo apt install --only-upgrade postgresql-16 # 5. 重启并验证 plperl 扩展状态 sudo systemctl restart postgresql sudo -u postgres psql -c "SELECT extname, extversion FROM pg_extension WHERE extname LIKE 'plperl%';"
4.3 第三步:无法立即升级时的临时缓解
若因业务窗口限制无法立即升级,可采取以下纵深防御措施降低攻击面。注意:这些措施是缓解而非修复,不能替代补丁。
- 移除或禁用 plperl:若业务未使用 plperl,直接
DROP EXTENSION plperl;并移除postgresql-plperl-*包。 - 收紧 CREATE 权限:撤销普通角色在 public schema 的 CREATE 权限,改为按需授权到独立 schema。
- 限制 plperlu:plperlu(不可信 Perl)本身具备更强能力,应确保仅超级用户可创建。
- 网络层收敛:通过 pg_hba.conf 限制来源 IP,减少网络可达面。
五、架构级反思:扩展生态的安全治理
PostgreSQL plperl UAF 漏洞再次提醒我们:数据库的安全边界不仅取决于内核,更取决于扩展生态。plperl、plpython、pljava 等过程语言扩展在提供灵活性的同时,也将数据库进程暴露在更复杂的运行时风险之下。作为架构师,建议在组织内建立以下治理机制:
- 扩展白名单:生产环境仅允许安装经过安全评审的扩展,plperl/plperlu 默认不启用。
- 最小权限模型:业务角色不应具备 public schema 的 CREATE 权限,函数创建走审批流程。
- 版本订阅:订阅 PostgreSQL 安全通告与 OSV 数据源,将小版本升级纳入月度运维窗口。
- 运行时防护:数据库进程以专用低权 OS 用户运行,配合 seccomp/AppArmor 限制系统调用面。
对于多租户 SaaS 架构,建议将“是否允许租户创建 plperl 函数”作为租户隔离等级的关键判定项。在共享数据库实例模式下,任何具备 CREATE 权限的租户都可能成为攻击跳板,物理隔离或独立实例是更稳妥的选择。
本文核心技术背景源自海外一线技术研究:
OSV – UBUNTU-CVE-2026-116307 (Canonical Ubuntu Security Notices)
PostgreSQL 官方安全通告 CVE-2026-116307
