一千条通向 root 的路

OpenAI 用一条五阶段 agent 流水线,花 121 万美元产出 948 个可用的 Linux 内核提权漏洞,平均一个 1279 美元,是历史赏金价的十六到五十五分之一。三个月前同一个框架只能找到 24 个。挖洞的产能刚刚被工业化,修洞的那一端还是每天 33 个的人类审核,这篇讲这个剪刀差落在谁头上。
十一月四日,OpenAI 的安全团队发了一篇文章,标题叫《A thousand fewer paths to root》。里面有一个数字组合值得单独拿出来看:一次十天的扫描,花掉约 121 万美元的 API 额度,产出 948 个可用的 Linux 内核本地提权漏洞,平均一个 1279 美元。
这不是"AI 又找到了一个洞"的故事。这是一条生产线的验收报告。真正的问题在数字背后:挖洞的产能刚刚被工业化,修洞的那一端没有。
这条流水线长什么样
先看产出端是怎么组织的。整条线分五段,每段是不同角色的 agent,每段的钱是分开记账的。
第一段叫 Scout,负责选靶子。它从四千多万行代码的内核里挑出 5,034 个低权限用户能够到的审计目标,约 280 万行,只占内核的百分之七。这一段只花了 3,426 美元。这个取舍是整条线在成本上成立的前提:攻击者拿不到的代码路径,不值得花 token。
第二段是 Analyst,对靶子做静态分析,12 小时产出 2,308 个候选发现。这段花得最多之一,31.6 万美元。
第三段是 triage,给每个候选写一个概念验证,扔进开了 KASAN 的隔离虚拟机里跑,30 小时复现出 1,724 个,复现率约四分之三。OpenAI 特意标注:剩下没复现的里面,只有 3.1% 是模型彻底分析错了,其余是需要更高权限或者更多时间。这段 7.3 万美元。
第四段做筛选,把复现的漏洞分成 1,238 个提权候选和 313 个信息泄露候选。4.4 万美元。
第五段是 ExploitDev,在开了 SMEP、SMAP、KASLR 这些标准防护的原版桌面发行版上打,产出 1,169 个能拿到 root 的 exploit 和 291 个信息泄露 PoC。这段最贵,77.6 万美元。变体分析归并之后,剩下 948 个互相独立的漏洞,每个配一个候选补丁。
最后一段是人。OpenAI 的研究员逐个核对 exploit、补丁和严重性评级,之后才交给内核维护者,按社区建议先报严重的那批,剩下的跟着 Patch the Planet 计划慢慢发。
一个洞一千三,这买卖算得过来
把成本和历史赏金摆在一起,结论很直接。Pwn2Own 给一个 Linux 内核提权 exploit 的历史赏金是 2 万美元,TyphoonPWN 是 7 万美元。948 个 exploit 按这个价折算,价值在 1,890 万到 6,630 万美元之间,是总成本的 16 到 55 倍。
单看成功的 exploit,平均成本只有 359 美元。花钱的大头是失败的那 671 次尝试,平均每次烧掉 3,922 美元。OpenAI 给的止损建议是卡一小时没进展就放弃。
它还拿 Google 的 kernelCTF 试了一次。那是个缩小了攻击面的加固内核,112 小时花了 3.3 万美元,拿到 21 个提权 exploit。做个对比:2026 年到发文时,kernelCTF 全年一共只接受了 26 个确认的 0day 提权洞。一条流水线十来天的产量接近全球赏金猎人干一年的量。
这些数字对防守方和攻击方同时成立,OpenAI 自己也是这么写的:找洞的成本已经低到双方都掏得起。
三个月,二十四到九百四十八
比绝对数字更该看的是斜率。
六月,GPT-5.5 Cyber 用同样的思路找到并验证了 24 个提权洞,那是 Patch the Planet 计划的起点。七月底,GPT-5.6 Sol 在同一套 harness 上跑出 166 个,花 51.4 万美元。十月中旬,一个还没发布的 cyber 模型跑出 948 个,花 121 万美元。
三次扫的都是当时的最新内核 master 分支,用的是同一套流程框架。也就是说目标没有变软,涨的是模型能力本身。三个月,不到六倍。OpenAI 在文里给的 takeaway 也直白:模型升级之后,应该回头重扫已经审计过的代码,新模型能找到旧模型漏掉的东西。
还有一条容易被略过的发现。他们抽样了 166 个 exploit 做技术分析,确认里面至少用了 10 种此前没人公开过的利用技术,包括新的对象喷射、新的页整理、五种 data-only 提权。这些技术拿给第三方内核利用专家看过,专家也认可是新的。模型不是在模式匹配已知漏洞形状,它在出新的招。细节出于安全考虑没有公开。
OpenAI 自己承认的两个软肋
这份报告可贵的地方是它没把失败藏起来。
第一个是测量翻车。早期一轮跑出来 200 个"成功拿到 root"的记录,人工复核发现其中 20 个根本不是通过被测的那个洞拿到的,而是 agent 顺手利用了另一个它自己发现的未知漏洞。拿到 root 不能证明你走的是哪条路。后来他们加了一道独立校验,确认最终 exploit 用的就是归因给它的那个洞。
第二个是瓶颈的位置。原话说得很清楚:难点已经不是发现,而是"确定哪些发现可复现、跨过了哪条安全边界、哪些值得维护者最早的关注"。第一段产出的候选数量,已经多到安全研究员手动审不过来的程度。
这句话翻译一下:发现端已经溢出,闸门在 triage 和人工审核这一段。
修复那一端是什么状况
Linux 内核的维护体系大部分是志愿者和公司工程师在撑,每个补丁都要人眼过。
今年秋天的 Kernel Recipes 大会上,内核资深维护者 Greg Kroah-Hartman 给过一组数:内核现在每天修 33 个安全问题,单个版本周期的修复量从长期的大约 500 个涨了约四倍,他把这个跳跃归因于 AI 工具。他当时的口径是"别慌,流程扛得住"。但他同时说了两件事:最好的模型仍有大约一半的时间是错的;他经手过一批 79 个 AI 报告的内核 bug,最后确认真实的只有 15 个,而这 15 个全都已经被修过了。八月他还发过一封内核邮件列表的公开信,宣布拒绝接收 drivers/staging 目录的 AI 补丁(测试过的安全修复除外),理由是"它们产出的结果至少有三分之一是彻头彻尾错误或有害的"。
OpenAI 这边的观察是另一面:十天的扫描跑完时,948 个洞里已经有 61 个在上游被修复、43 个有 staging 补丁、299 个在公开讨论中。其中不少修复来自其他同样在挖这些洞的贡献者。两边说的都是实话:维护者在拼命追,同时涌入的报告量已经把审核能力当成了瓶颈资源。
一边是成本每三个月翻几倍的发现产能,一边是线性爬坡的人类审核产能。这个剪刀差就是接下来所有故事的主线。
对跑生产环境的人意味着什么
OpenAI 报告里有一句话,我觉得是全文最该抄下来贴墙上的:对资源充足的威胁行为者而言,跟不上更新的系统上的低权限账号,应被视为在功能上等价于 root。
我上一篇文章写群晖 NAS 被勒索,用的两个洞补丁在四十二天前就发了。那篇文章的结论在这份报告之后要加一个权重:以后洞出现的速度和公开的速度都会更快,"等一个方便的时间再更新"这个选项正在被从菜单上划掉。
具体到运维,三件事值得现在就做:
把内核和基础库的更新从"事项"升成"流水"。 手动排窗口的更新节奏是按月计的,而这条新闻告诉你洞的产出节奏开始按天计。能自动化的更新通道(发行版自动安全更新、容器基础镜像的每日重建)不要再犹豫。
清点你真正跑的是什么内核。 OpenAI 扫的是 master 分支。你的生产环境跑的可能是某个 LTS、某个发行版定制分支、某个嵌入式 fork,这些分支得不到同等的研究注意力,洞在那里躺得更久。知道自己在跑什么,是评估暴露面的第一步。
重新看一遍你的低权限边界。 容器、多租户机器、给了员工 shell 的服务器,过去依赖"内核隔离兜底"的假设,现在要按"低权限可能随时变 root"来重新设计:降权限的收益打折,隔离网络和最小化文件访问的收益上调。
一个能跑的推演
下面这个脚本把两边的产能放进同一个模型里跑一遍。左边是发现端,用 OpenAI 三代模型的数据标定增长曲线;右边是修复端,用 Kroah-Hartman 给的每天 33 个做消化速率。你要输入的只有两样:机器台数,和你的平均补丁安装延迟。
#!/usr/bin/env python3
"""发现产能 vs 修复产能:你的 fleet 会不会被淹没。
用法:
python3 fleet_exposure.py # 用内置示例跑
python3 fleet_exposure.py --fleet 40 --delay 9 # 你的数字
"""
from __future__ import annotations
import argparse
import math
# 发现端产能标定:OpenAI 三代模型单轮产出(个/轮)
# (月份, 单轮 LPE 产出)
DISCOVERY_RUNS = [
("2026-06", 24), # GPT-5.5 Cyber
("2026-07", 166), # GPT-5.6 Sol
("2026-10", 948), # 即将发布的 cyber 模型
]
# 修复端消化速率:Kroah-Hartman,内核每天修 33 个安全问题
FIX_RATE_PER_DAY = 33.0
# 假设一轮扫描的发现会在 30 天内陆续公开(补丁 + 报告 + 讨论)
DISCLOSURE_WINDOW_DAYS = 30
def monthly_growth() -> float:
"""按三次产出拟合产能的月增长率。"""
months = [0.0, 1.0, 4.0] # 6 月、7 月、10 月
outputs = [float(n) for _, n in DISCOVERY_RUNS]
logs = [math.log(o) for o in outputs]
n = len(months)
mx, my = sum(months) / n, sum(logs) / n
num = sum((x - mx) * (y - my) for x, y in zip(months, logs))
den = sum((x - mx) ** 2 for x in months)
slope = num / den
return math.exp(slope) # 每月倍数
def discovery_per_month(when: float) -> float:
"""when = 距 2026-10 的月数,返回该月单轮产出规模。"""
return DISCOVERY_RUNS[-1][1] * (monthly_growth() ** when)
def public_flow_per_day(when: float) -> float:
"""每月新公开的漏洞摊到每天(假设每月跑一轮)。"""
return discovery_per_month(when) / DISCLOSURE_WINDOW_DAYS
def steady_backlog(when: float) -> float:
"""公开速率超过修复速率后,堆起来的存量。"""
inflow = public_flow_per_day(when)
if inflow <= FIX_RATE_PER_DAY:
return 0.0
# 保守口径:只按超出部分逐月累积
months_elapsed = max(when, 0.0)
return (inflow - FIX_RATE_PER_DAY) * DISCLOSURE_WINDOW_DAYS * (months_elapsed + 1)
def fleet_exposure(fleet: int, delay_days: float, when: float = 0.0) -> dict:
"""估算:任意时刻,fleet 里装着已知漏洞的机器期望台数。
when = 距 2026-10 那轮的月数,0 表示按当前公开速率算。
"""
# 每天公开的"可利用"漏洞速率(只算提权这一类)
daily_new = public_flow_per_day(when)
# 补丁发布到装上之间,每台机器每天暴露在新漏洞下
live_bugs_per_machine = daily_new * delay_days
exposed = fleet * min(1.0, live_bugs_per_machine / 50.0)
return {
"月单轮产出": round(discovery_per_month(when)),
"每天新公开": round(daily_new, 1),
"修复端日消化": FIX_RATE_PER_DAY,
"单机滞留已知洞": round(live_bugs_per_machine, 1),
"期望暴露机器": round(exposed, 1),
"暴露占比": f"{exposed / fleet:.0%}",
}
def render(fleet: int, delay: float) -> None:
g = monthly_growth()
now_flow = public_flow_per_day(0.0)
later_flow = public_flow_per_day(2.0)
print(f"发现端月增长率(按三次公开数据拟合): x{g:.2f}\n")
print(f"当前一轮月产出 {DISCOVERY_RUNS[-1][1]} 个,摊到每天 {now_flow:.1f} 个,"
f"修复端日消化 {FIX_RATE_PER_DAY:.0f} 个")
print(f"外推两个月后每天新公开 {later_flow:.1f} 个(指数外推,勿当预测)\n")
for label, d in [("当天就装", 0.5), ("拖一周", 7.0), ("拖一个月", 30.0)]:
r = fleet_exposure(fleet, d)
print(f"fleet={fleet} 补丁延迟={label:<6} "
f"每天新公开 {r['每天新公开']:>5} 个 "
f"单机滞留 {r['单机滞留已知洞']:>5} 个 "
f"暴露 {r['暴露占比']:>4}")
print(f"\n两栏对照的读法:公开速率一旦长期高于修复速率,存量只会越堆越高,"
f"你的延迟天数就是拿这个存量当赌注。")
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--fleet", type=int, default=50, help="机器台数")
ap.add_argument("--delay", type=float, default=9.0, help="平均补丁安装延迟(天)")
args = ap.parse_args()
render(args.fleet, args.delay)
r = fleet_exposure(args.fleet, args.delay)
print(f"\n你的配置:{args.fleet} 台,平均延迟 {args.delay} 天,"
f"期望同时暴露 {r['期望暴露机器']} 台({r['暴露占比']})。")
if __name__ == "__main__":
main()跑出来的结果长这样:
发现端月增长率(按三次公开数据拟合): x2.32
当前一轮月产出 948 个,摊到每天 31.6 个,修复端日消化 33 个
外推两个月后每天新公开 169.9 个(指数外推,勿当预测)
fleet=50 补丁延迟=当天就装 每天新公开 31.6 个 单机滞留 15.8 个 暴露 32%
fleet=50 补丁延迟=拖一周 每天新公开 31.6 个 单机滞留 221.2 个 暴露 100%
fleet=50 补丁延迟=拖一个月 每天新公开 31.6 个 单机滞留 948.0 个 暴露 100%
两栏对照的读法:公开速率一旦长期高于修复速率,存量只会越堆越高,你的延迟天数就是拿这个存量当赌注。
你的配置:50 台,平均延迟 9.0 天,期望同时暴露 50.0 台(100%)。先看第二行和第三行,这是整个模型的题眼。按十月那轮的产出摊到每天,是 31.6 个新公开的洞;修复端按 Kroah-Hartman 的口径每天消化 33 个。今天这两栏刚好持平,维持者的日修复量是被 AI 撬起来的这个事实,本身就是产能对撞的证据。而按拟合出的月增长率 2.32 倍外推两个月,公开速率就变成每天 170 个,是修复端容量的五倍多。
模型里的口径要交代清楚。月增长率 2.32 是拿 24、166、948 三个点拟合的指数曲线,三个点拟一条指数线,外推越远越不可信,两个月那一行是敏感性演示不是预测。单机滞留是"每天新公开 × 延迟天数"的粗乘积,它假设公开的洞全部影响你的内核,实际只会是一部分,所以这个数是上界。暴露占比把 50 个滞留洞封了顶,在这个粗模型下,任何拖一周以上的更新节奏都会让"有已知洞未修"成为常态而不是事件。
但结论不依赖这些口径的精度。就算把月增长率砍到 1.5,把"每天新公开"打个对折,几轮之内公开速率仍然会越过任何人类审核体系能消化的水平。那时候分出安全与不安全的,不再是"有没有洞",只剩"装没装上"。
回到那句结论
这份报告实际改变的东西,是它把一个行业默认假设戳穿了。过去大家默认漏洞发现是稀缺的:一个洞从存在到被人找到,中间隔着研究员的工时,这个间隔给防守方留出了时间。现在发现端可以按 API 账单伸缩,那个间隔正在坍缩。
OpenAI 在文末说,按目前前沿模型发现漏洞的速率,自动化补丁生成是让软件保持安全的唯一现实路径。这句话的另一半没写出来:在那条路跑通之前,中间这段路上,装补丁的速度就是安全本身。
上一篇文章的结尾我说,补丁发出来的那一刻漏洞就公开了,你的设备什么时候装上它是你唯一能决定的变量。现在给这句话再加一个时间戳:留给这个变量的窗口,正在按每个月大约二分之一的速度缩短。