pgcrypto 里藏了二十年的两个 RCE,AI fuzzer 一个周末就揪出来了

2026-05-16 24 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

PostgreSQL 的 pgcrypto 扩展,很多项目拿来加密存储密码、签名数据,默认就觉得"加密模块应该是安全的"。结果:两个远程代码执行漏洞从 2005 年一直活到 2025 年,二十年无人发现,最后被一个 AI 驱动的 fuzzer 在一个周末内挖了出来。这件事值得每个用 PostgreSQL 的人认真看一眼。

pgcrypto 是什么,漏洞在哪

pgcrypto 是 PostgreSQL 的官方贡献扩展,提供 encrypt()decrypt()crypt()gen_salt() 等函数,直接在 SQL 层做加密运算。很多应用把用户密码哈希、敏感字段加密都交给它处理,逻辑上这些函数接收用户可控的输入参数。

问题就出在这里——这些函数的 C 实现中存在内存安全缺陷。两个 RCE 漏洞的根因都指向 pgcrypto 内部对输入参数的处理:特定构造的参数可以触发缓冲区溢出或越界写入,最终让攻击者在数据库服务器进程里执行任意代码。而数据库进程通常以高权限运行,这意味着拿到 RCE ≈ 拿到服务器。

更关键的一点:这些漏洞不需要超级用户权限就能触发。只要一个普通用户能执行 pgcrypto 的函数(而很多应用恰恰允许这一点),攻击路径就成立了。

二十年没被发现,为什么

这两个 bug 不是"刚写就出问题然后被修了",而是从 2005 年 pgcrypto 的某次重构引入后,一直安静地存在了二十年。原因值得深思:

  • pgcrypto 代码路径偏冷。加密函数的内部实现走的是 C 层的底层密码学库,常规的 SQL 层测试很难覆盖到边界条件。
  • 人工审计的盲区。密码学代码审计通常关注算法正确性、密钥管理,而不是内存安全。C 代码的缓冲区边界检查,在加密模块里反而容易被忽略。
  • 传统 fuzzer 的局限。对 SQL 函数做 fuzz 需要理解 PostgreSQL 的类型系统和调用协议,传统通用 fuzzer 很难高效构造出能深入 pgcrypto 内部路径的输入。

二十年里,无数人用过 pgcrypto,无数安全审计扫过 PostgreSQL,这两个漏洞就是没被看见。

AI fuzzer 为什么一个周末就搞定了

这次发现的主角是 AI 驱动的 fuzzer——它能理解 PostgreSQL 的函数签名、参数类型和语义,自动构造出针对性的测试输入。传统 fuzzer 面对带类型的 SQL 函数接口时,大量随机输入在类型检查阶段就被拒绝了,根本进不到 C 层的脆弱路径。AI fuzzer 的优势在于:

  • 理解函数参数的语义约束,构造"合法但极端"的输入
  • 能识别哪些输入组合更可能触发深层代码路径
  • 快速迭代,一个周末跑了远超人工能覆盖的路径数量

这不是"AI 比人聪明"的故事,而是"AI 能在特定约束下高效探索组合空间"的故事。二十年人工没覆盖到的边界条件,AI fuzzer 用规模化的语义感知搜索撞开了。

检查你的暴露面并修复

下面是一套可以直接跑的检查流程。先确认你的 PostgreSQL 是否安装了 pgcrypto,再确认版本是否已修复。

# 1. 检查 PostgreSQL 版本(两个 RCE 在 17.4 / 16.8 / 15.12 / 14.17 / 13.20 中被修复)
pg_config --version

# 或者通过 SQL 查询
psql -c "SELECT version();"
-- 2. 检查 pgcrypto 是否被安装和使用
-- 查看哪些数据库加载了 pgcrypto
SELECT datname, extname, extversion
FROM pg_extension
JOIN pg_database ON pg_database.oid = pg_extension.extowner
WHERE extname = 'pgcrypto';

-- 如果上面 JOIN 不对,直接查当前数据库
SELECT extname, extversion FROM pg_extension WHERE extname = 'pgcrypto';

-- 3. 查看谁有权限执行 pgcrypto 函数(这是风险的关键)
SELECT n.nspname, p.proname, pg_get_userbyid(p.proowner) AS owner,
       acl.acl
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
JOIN pg_extension e ON e.extoid = p.oid
LEFT JOIN LATERAL aclexplode(p.proacl) AS acl ON true
WHERE e.extname = 'pgcrypto';

-- 简化版:看 public schema 下 pgcrypto 函数的执行权限
SELECT proname, proacl
FROM pg_proc
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
  AND proname IN ('encrypt', 'decrypt', 'crypt', 'gen_salt',
                   'pgp_sym_encrypt', 'pgp_sym_decrypt',
                   'pgp_pub_encrypt', 'pgp_pub_decrypt');
# 4. 升级 PostgreSQL(最直接的修复方式)
# Debian/Ubuntu:
sudo apt update && sudo apt install postgresql-17=17.4*

# RHEL/CentOS:
sudo yum update postgresql17-server

# Docker: 拉取已修复版本的镜像
docker pull postgres:17.4
# 然后重建容器,注意数据卷挂载
-- 5. 如果暂时无法升级,限制 pgcrypto 函数的执行权限
-- 从 PUBLIC 角色收回执行权限(阻止普通用户调用)
REVOKE EXECUTE ON FUNCTION encrypt(bytea, bytea, text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION decrypt(bytea, bytea, text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION crypt(text, text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION gen_salt(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION gen_salt(text, integer) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pgp_sym_encrypt(text, text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pgp_sym_decrypt(bytea, text) FROM PUBLIC;

-- 只授权给真正需要的角色
GRANT EXECUTE ON FUNCTION crypt(text, text) TO app_auth_role;
GRANT EXECUTE ON FUNCTION gen_salt(text) TO app_auth_role;

注意:收回 PUBLIC 权限后,确保你的应用连接使用的数据库角色已被显式授权,否则加密函数调用会直接报权限错误。

更大的教训:依赖链里的"信任惯性"

pgcrypto 的事不是孤例。它暴露的是一个普遍问题:我们对"官方扩展"和"密码学模块"有信任惯性。觉得是 PostgreSQL 官方提供的、名字里带 crypto 的,就默认安全。但现实是:

  • 官方扩展的 C 代码同样有内存安全风险,审计深度并不比第三方更可靠
  • 密码学模块的安全假设是"算法正确",但实现层的缓冲区溢出是另一层问题
  • 冷门代码路径的漏洞寿命特别长,因为没人系统性地去 fuzz 它

AI fuzzer 的出现改变了经济学:以前对复杂接口做深度 fuzz 成本极高,现在成本大幅下降。这意味着未来几年,类似的"二十年老 bug"会被批量发现。对使用者来说,几件事值得现在就做:

  1. 升级到已修复版本——这是最彻底的方案,不要拖。
  2. 审计扩展权限——默认 PUBLIC 有 EXECUTE 权限的函数,收回它,按最小权限原则重新授权。
  3. 盘点所有加载的扩展——不只是 pgcrypto,任何 C 语言写的 PostgreSQL 扩展都有类似的内存安全风险面。列出清单,逐个查版本和已知漏洞。
  4. 考虑把加密计算移到应用层——数据库做加密存储,意味着数据库进程必须处理用户可控的密文输入。如果加密/解密在应用层完成,数据库只存结果,攻击面就缩小了。这不是万能方案,但值得评估。
  5. 关注 AI fuzzing 的进展——这类工具正在快速成熟,未来可能成为标准的安全测试手段。了解它的能力边界,知道它能发现什么、不能发现什么。

二十年两个 RCE,一个周末被揪出。这不是戏剧性故事,是安全工程里"覆盖度不够"的常态。区别只是,现在有了更便宜的覆盖手段。


相关推荐