克里斯·罗尔夫
过去一年,我们发布了许多数据,表明前沿 AI 模型完全有能力发现并利用安全漏洞。然而,展示此前未知的漏洞可能如何被利用,只是前沿 AI 模型在网络防御方面所能做之事的开端。在这篇文章中,我们总结了我们在自动化攻击面分析与缩减方面的一些初步研究:即使用 AI 模型绘制可达代码,然后移除或限制对特定部署无需暴露的任何内容的访问的过程。
自动化攻击面分析和缩减可以在近期切实改善已部署软件的安全性。仅通过发现和修复单个漏洞来保障软件安全,其可扩展性很差:即便修复了一个严重漏洞,也无法说明还剩哪些漏洞。相比之下,减少代码可达性可以降低未知漏洞的严重性,包括可能正被积极利用的漏洞。这些技术并不会消除代码中的未知漏洞,但它们确实使漏洞更难被触达,从而降低其严重性。最小化代码库的攻击面,无需发现或修复任何单个漏洞,就能带来更好的安全性。
下面,我们预览一些数据,这些数据来自使用 Claude 将攻击面缩减技术应用于大型开源代码库的过程。我们这样做的目的是表明,Claude 可以通过我们概述的方法,以有意义且经济的方式改善软件安全性。具体来说,我们在 25 个以上项目(涵盖多种语言)上运行了 Claude,它发现了 493 个问题。基于这些结果,我们讨论开发者如何将类似技术应用于自己的代码。
减少代码库攻击面的方法
随着时间的推移,软件往往会变得更加复杂,新的抽象层、新的依赖项和新功能会慢慢层层叠加在一起。这常常会导致它的运行方式与原开发者的意图大相径庭。这也是攻击者往往如此成功的一个重要原因:他们研究的正是某段代码在实际中如何运行,而不是其开发者希望或设计它如何运行。
自动化攻击面分析与缩减技术旨在发现不必要的攻击面,然后将这些代码移到适当的门控之后。这可能涉及将代码从预认证阶段移到认证后阶段,从零点击移到一次点击,从默认开启移到显式选择加入,或从协议的第一条消息移到会话状态机更深处。有些代码本来就应当在上述阶段之前可达,但很多代码并非如此;而且,考虑到跨数千或数百万行代码的代码频繁变更所带来的复杂性,功能的不必要暴露往往不被注意到。
减少代码攻击面的方法有很多。在我们自己的研究工作中,我们特别关注了三种方法:
默认配置。我们发现有些代码在其交付时的默认配置中是可访问的,但本来并不打算供任何默认功能使用。
配置交互。我们发现了一些选项对,它们单独看时似乎合理,但一起配置时会导致问题(例如,静默禁用某项安全控制,或在功能代码仍可触达的情况下使该功能的行为处于未定义状态)。
早期可达性。我们识别出了安全敏感且复杂的代码,这些代码要么在适当的安全控制(例如身份验证)之前运行、无需受害者交互,要么在协议状态转换之前运行。
我们想了解 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 地址。这意味着拒绝规则永远不会触发,列表会失败开放。一些受影响的项目在文档中提到,主机名规则依赖于启用 DNS 查询,但没有任何项目说明以这种方式编写的拒绝列表会具有失败开放行为。
第三,Claude 经常发现两个配置选项会以静默丢弃安全检查的方式冲突。在一个案例中,管理员可以要求每个连接的客户端用证书证明其身份。在另一个案例中,代码本意只是将该检查延迟到需要时进行,但实际上完全禁用了该检查。因此,一个配置为仅接受受信任客户端的服务器会接受任何人的连接。这使大量请求处理代码(本应位于证书检查之后)暴露给未认证客户端。在默认设置下,该软件接受这两个配置选项同时启用,且没有任何警告;文档从未提到证书要求已被丢弃。这组相互冲突的配置选项已在软件中存在了 21 年。
最后,Claude 发现了一个项目中的问题:该项目默认关闭认证(并期望管理员将其开启)。要求 TLS 客户端证书的选项在项目的大多数网络端点上都会生效,但在其他少数端点上会被静默忽略(这些端点通过使用不同命名空间的路径加载其 TLS 设置)。这意味着没有证书的客户端也能在受影响的端点上完成 TLS 握手并打开会话。不会记录任何关于该设置被忽略的日志。该选项是否生效取决于管理员在配置中使用的命名空间。设置名称的一种形式在每个端点上都能生效,但较短形式——大多数示例使用的形式——在受影响的端点上会被丢弃。
案例研究:OpenSSH
令人惊讶的是,按占总代码行数的百分比衡量,Claude 实现攻击面缩减幅度最大的项目是 OpenSSH。OpenSSH 是一个高质量 C 代码库,提供安全的远程访问。Claude 在 10.4 版本中追踪了 SSH 守护进程的输入路径,并发现约 4,500 行用于 ML-DSA-44(一种后量子签名算法)的新增实验性代码,未认证客户端在默认配置下可以触达这些代码。ML-DSA 默认未启用,但无论是否启用,这些代码都可被触达,即使管理员明确排除了该算法。守护进程会解析客户端发送的任意密钥类型(对于证书,还会验证内嵌的 CA 签名)。之后它才检查该密钥类型是否被允许。当配置未启用时,让这段复杂且内存不安全的代码可被触达没有任何正当理由。在默认 sshd 中,ML-DSA-44 的这 4,500 行代码约占 OpenSSH 自身预认证代码的 16%。我们连同补丁一起向 OpenSSH 开发者报告了此问题。10.5 版本将检查移到了解析之前。
Claude 随后指出了 10.5 检查中的两个缺口。第一个是客户端声称的算法与其实际发送的算法不匹配。检查查看的是客户端宣告的算法名称,但解析器是由密钥数据块内部嵌入的类型字符串选择的。客户端可以宣告 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 技能,并正在努力将其集成到 OSS-Scanner 中。该技能会对代码库运行三轮。每次都会从头开始(使用空的上下文窗口),除了它会从前一轮收到的一小段上下文之外。
在第一遍中,该技能会检查是否存在这样的代码:默认配置允许其被到达,但任何预期功能都不会使用它。在第二遍中,它会检查在任何配置下,在任何硬性门控(如身份验证)之前或协议状态机入口点之前运行的代码。最后,第三遍中,该技能会评估哪些配置组合会静默关闭某项安全控制或降低其有效性。在每一遍中,Claude 都会根据代码创建入口点清单,并通过程序的动态跟踪来确认每一项发现。对于默认配置相关的发现,它会设计并实现一个门控,并确保这不会改变程序的默认行为。它通过针对基线运行项目自身的测试套件(以及黑盒回归测试套件)来做到这一点;它要求修改后的构建产生完全相同的结果。任何会改变非默认配置的门控都会被搁置,交由人类决定。对于另外两遍,它会提出诸如重新排列配置检查顺序、添加防护以强制状态顺序,或在配置加载时拒绝该组合之类的更改。即使不使用这个特定技能,我们认为这里概述的广泛方法也很可能是有效的。
本文概述的技术只能让人非常有限地了解攻击面缩减方面可能实现的目标。例如,Claude 还能够分析运行时日志并将其与配置进行比较,以确保只启用所需的功能:禁用未使用的解析器、遗留协议和仪表板能以很小的成本降低安全风险。
我们鼓励各组织探索使用 Claude 来分析和缩减其所依赖软件的攻击面。我们预计,这将成为开发者以可衡量方式提升世界上现有代码安全性最实用的方法之一。