Chris Rohlf
在过去一年里,我们发布了大量数据,表明前沿 AI 模型完全有能力发现并利用安全漏洞。然而,展示此前未知的漏洞可能如何被利用,只是前沿 AI 模型在网络防御方面所能做之事的开端。在这篇文章中,我们总结了我们在自动化攻击面分析与缩减方面的一些初步研究:即使用 AI 模型来梳理可达的代码,然后移除或限制对给定部署无需暴露的任何内容的访问。
自动化攻击面分析与缩减可以在短期内切实提升已部署软件的安全性。仅通过发现和修复单个漏洞来保障软件安全,其可扩展性很差:即使修复了一个严重漏洞,也无法告诉你还剩下哪些漏洞。相比之下,缩减代码可达性可以降低未知漏洞的严重程度,包括那些可能正被积极利用的漏洞。这些技术并不会消除代码中的未知漏洞,但它们确实让漏洞更难被触达,从而降低其严重程度。最小化代码库的攻击面,无需发现或修复任何单个漏洞,就能带来更好的安全性。
下面,我们预览一些使用 Claude 将攻击面缩减技术应用于大型开源代码库所收集到的数据。我们这样做的目的是表明,Claude 可以通过我们概述的方法,切实且经济地提升软件安全性。具体而言,我们在 25 个以上项目(涵盖多种语言)上运行了 Claude,它发现了 493 个问题。基于这些结果,我们讨论了开发者如何将类似技术应用于自己的代码。
缩减代码库攻击面的方法
随着时间的推移,软件有一种变得越来越复杂的倾向,新的抽象层、新的依赖和新的功能慢慢层层叠加。这往往会导致它的实际行为与最初开发者的意图大相径庭。这也是攻击者常常如此成功的一个重要原因:他们研究的是一段代码在实践中究竟如何运行,而不是其开发者意图或设计它如何运行。
自动化攻击面分析与缩减技术旨在发现不必要的攻击面,然后把那部分代码移到适当的关卡之后。这可能涉及把代码从认证前移到认证后、从 0 点击移到 1 点击、从默认开启移到显式选择加入,或从协议的第一条消息移到会话状态机更深处。有些代码本就应当在这些阶段之前可达,但很多并非如此,而且考虑到成千上万乃至数百万行代码变动所带来的复杂性,功能的不必要暴露往往无人察觉。
减少代码攻击面的方法有很多。在我们自己的研究中,我们特别关注了三种方法:
- 默认配置。我们识别出了在出厂默认配置下可达、但并非任何默认功能所意图使用的代码。
- 配置交互。我们找到了这样一些选项对:单独看都合理,但配置在一起时就会引发问题(例如,悄悄禁用某项安全控制,或让某个功能的行为处于未定义状态而其代码仍然可达)。
- 早期可达性。我们识别出了安全敏感且复杂的代码,它们在适当的安全控制(如认证)之前运行、无需受害者交互,或在协议状态转换之前运行。
我们想了解 Claude 能在多大程度上利用这三种攻击面描述,在大型复杂代码库中发现真实问题。我们分析了 25 个开源项目(代码总量超过五百万行),涵盖 C、C++、Java、PHP 和 Python 等语言。Claude 在这些项目中梳理出超过 17,000 个配置选项。其中 3,842 个与安全相关的控件或功能配置有关。
出于研究目的,我们将这些开源项目分为三大类:
- 公开。这类项目旨在服务于任何能够访问它们的用户,或消费无法识别来源方所编写的数据。(例如解析上游应答的 DNS 解析器,或解析源站响应的代理。)
- 受限。这类项目旨在服务于已认证用户(例如持有密码、密钥或证书的用户,或已注册的设备)。软件仍允许任意客户端请求,但在用户完成认证之前不提供任何功能。
- 私有。这类项目构建并部署用于服务运营方自己的私有环境。在这种情况下,添加认证层之类的控制是可选的,但不在软件的范围之内。
这些分类有助于为我们的结果提供背景。例如,我们预计公开类代码库中有相当大比例在默认配置下即可访问(因为它被设计为服务任何用户),但受限类项目的情况恰恰相反。
Claude 的发现
Claude 使用上述三种方法发现了 493 个问题。从结果中我们注意到几个突出的主题。
首先,Claude 发现所有十一个受限项目的代码都存在无意中可达的情况(尽管这些代码在那些项目的总代码行数中占比并不显著)。具体而言,Claude 在受限组群的默认配置中发现了 93 个不必要的预认证暴露面、37 处在认证之前就执行代码或记录状态的位置,以及 61 种会悄然削弱管理员所配置的预认证控制措施的配置组合。这些问题单独来看都不会立即被利用,但它们造成的攻击面远超必要范围。

按类别分列每种技术所发现问题的明细。在我们评估的 25 个项目中,10 个为公开项目、11 个为受限项目、4 个为私有项目。受限组群包含最令人意外的结果,早期可达性发现数量很高。
其次,Claude 在缓解配置交互问题方面的工作产生了一些最有趣、最具影响力的发现(涵盖所有项目类型)。Claude 发现了 162 种安全敏感的配置组合,这些组合被接受时既没有报错也没有警告,且没有任何关于其交互预期行为的文档说明。在其中 94 种情况下,该组合悄然关闭了一项安全控制措施。另外 68 种情况则是同一安全机制内部的不一致(例如,某个监听器或登录路径仍然可达,尽管配置声称它已被禁用;两项设置相互冲突,而哪一项优先取决于它们在配置文件中的顺序;或者管理员配置的某项保护措施不起作用,而其代码仍然可达)。虽然其中一些配置组合可能属于非标准或很少使用的用法,但没有任何机制能阻止这些项目的管理员以这种方式配置和部署代码。
Claude 还发现了一个特定的模式,它在四个互不相关的项目中各自独立出现。其运作方式如下。管理员会使用主机名或通配符模式(而非 IP 地址)为远程客户端编写访问规则。当客户端连接时,服务器会将这条规则与客户端的主机名进行比对。服务器是否能获知该主机名,取决于一个控制反向 DNS 查询的独立选项(大多数项目都将其配置为关闭)。在查询关闭的情况下,服务器只能拿到客户端的 IP 地址。服务器会将规则与该地址按纯文本进行比对。但主机名模式永远无法匹配 IP 地址。这意味着拒绝规则永远不会触发,该列表会失效开放(fail open)。部分受影响的项目在文档中提到,主机名规则依赖 DNS 查询处于启用状态,但没有任何一个项目说明以这种方式编写的拒绝列表会具有失效开放的行为。
第三,Claude 经常发现两个配置选项会以某种方式相互冲突,从而静默地丢弃某项安全检查。在一个案例中,管理员可以要求每个连接的客户端用证书证明自己的身份。在另一个案例中,代码本意只是将这项检查推迟到需要时才执行,但实际上却完全禁用了该检查。因此,一台被配置为只接受受信任客户端的服务器会接受任何人的连接。这使得大量请求处理代码(本应位于证书检查之后)暴露给了未认证的客户端。该软件在默认设置下同时接受这两个配置选项且没有任何警告;文档也从未提及证书要求已被丢弃。这组相互冲突的配置选项在该软件中已存在了 21 年。
最后,Claude 在一个项目中发现了问题:该项目默认关闭身份验证(并期望管理员手动开启)。要求 TLS 客户端证书的选项在项目的大多数网络端点上都能生效,但在另外少数几个端点上却被静默忽略(这些端点通过不同命名空间的路径加载其 TLS 设置)。这意味着没有证书的客户端也能完成 TLS 握手,并在受影响的端点上打开会话。对于被忽略的设置,不会有任何日志记录。该选项是否生效取决于管理员在配置中使用的命名空间。设置名称的一种形式在所有端点上都能生效,但较短的形式——大多数示例使用的形式——在受影响的端点上会被丢弃。
案例研究:OpenSSH
令人意外的是,按占总代码行数的百分比衡量,Claude 实现攻击面缩减幅度最大的项目是 OpenSSH。OpenSSH 是一个高质量的 C 代码库,提供安全的远程访问。Claude 追踪了 10.4 版本中 SSH 守护进程的输入路径,发现约 4,500 行新增的实验性代码,用于 ML-DSA-44(一种后量子签名算法),未认证的客户端在默认配置下即可触达这些代码。ML-DSA 默认并未启用,但无论是否启用,这些代码都可被触达,即使管理员明确排除了该算法也是如此。守护进程会解析客户端发送的任何密钥类型(对于证书,它还会验证内嵌的 CA 签名)。直到那之后,它才检查该密钥类型是否被允许。这段复杂且内存不安全的代码在配置未启用时却可被触达,没有任何正当理由。ML-DSA-44 的这 4,500 行代码约占默认 sshd 中 OpenSSH 自身预认证代码的 16%。我们连同补丁一起向 OpenSSH 开发者报告了此问题。10.5 版本将检查移到了解析之前。
Claude 随后指出了 10.5 检查中的两个缺口。第一个是客户端声称的内容与其实际发送的内容不匹配。该检查查看的是客户端所宣称的算法名称,但解析器却是由密钥 blob 内嵌的类型字符串选择的。客户端可以宣称 ssh-ed25519、通过检查,却仍然发送一个会被解析的 ML-DSA 密钥。
第二个缺口出在 10.5 检查的证书上。证书密钥类型默认是启用的。给定一个由攻击者提供的证书,sshd 会反序列化其中内嵌的 CA 公钥,并在查询配置之前,对任何受支持的算法用它执行完整的签名验证。用户密钥签名路径被正确地限制在允许连接服务器的公钥列表之后,但 CA 签名路径没有。这使得二进制中每一个公钥签名验证器都可被任何能完成密钥交换的客户端触达。我们构建了一个概念验证,并与维护者一起迭代了若干版修复方案。最终的改动把检查放进了解析器内部。编码的密钥类型和证书的 CA 签名算法现在会与配置的允许列表进行比对在相应的反序列化或签名验证之前。
该修复移除了在默认构建中未认证客户端此前本可触达的约五分之一的 OpenSSH 代码。其中几乎全部都是新的 ML-DSA 代码,如今即便客户端谎报其密钥类型,或把 ML-DSA CA 密钥藏在一个原本可接受的证书里,也无法触达这些代码。使用允许的 CA 算法签名的证书仍会被解析和验证,但检查现在运行在解析之前。维护者已在版本10.6中发布了此处描述的改动。
OpenSSH 是世界上被审计最密集的代码之一。如果不必要的可触达性在那里都能存活,那它在任何地方都能存活。这表明,缩减代码库的攻击面很可能在许多重要代码库中带来回报。
结论
要衡量任何给定代码库中存在多少未知漏洞,或许是不可能的。而且有大量数据表明,攻击者只要时间足够,就能也确实会找到这些漏洞(尤其是在配备前沿 AI 模型的情况下)。但防御者若能减少代码中不必要的攻击面,就能一次性降低该代码中所有未知漏洞带来的风险——包括那些已经在被悄悄利用的漏洞。OpenSSH 是世界上被审计最多的 C 代码之一。然而它却发布了约 4,500 行实验性的签名解析代码,攻击者在任何配置检查之前就可能触达这些代码。
我们已将上文描述的攻击面削减技术整合为一个可复用的 Claude skill,并正着手将其集成到OSS-Scanner。该 skill 会对代码库运行三轮扫描。每一轮都从全新状态开始(上下文窗口为空),只接收上一轮传入的一小段上下文。
在第一遍中,该技能检查是否存在默认配置允许到达、但未被任何预期功能使用的代码。在第二遍中,它检查在任何配置下,在任何硬性关卡(如身份验证)之前或协议状态机入口点之前运行的代码。最后,第三遍,该技能评估哪些配置组合会静默关闭某项安全控制或降低其有效性。在每一遍中,Claude 都会从代码中建立入口点清单,并通过程序的动态跟踪确认每一项发现。对于默认配置的发现,它会设计并实现一个关卡,并确保这不会改变程序的默认行为。它通过针对基线运行项目自身的测试套件(以及一个黑盒回归套件)来做到这一点;它要求修改后的构建具有完全相同的结果。任何会改变非默认配置的关卡都会被搁置,交由人类决定。对于另外两遍,它会提出诸如重新排序配置检查、添加守卫以强制状态顺序,或在配置加载时拒绝该组合等更改。即使不使用这个特定技能,我们认为这里概述的广泛方法论也很可能是有效的。
本文中概述的技术只能让我们对攻击面缩减的可能性窥见一斑。例如,Claude 还能够分析运行时日志并将其与配置进行比较,以确保只启用了所需的功能:禁用未使用的解析器、遗留协议和仪表板能以很小的代价降低安全风险。
我们鼓励各组织探索使用 Claude 来分析和缩减其所依赖软件的攻击面。我们预计这将是开发者能够切实改善世界上已经在运行的代码安全性最实用的方式之一。