漏洞搜寻:零知识、充分偏执与反向凝视的AI - ZKSECURITY

本文探讨了AI在零知识电路和应用程序中发现漏洞的能力,zkSecurity开发了一款名为SnarkSentinel的AI驱动的ZK审计工具。

环中人

过去一年,我们一直在探索一个大问题:AI 真的能在零知识电路和应用中发现好的 bug 吗?如果可以,这对我们审计员意味着什么?我们的工作是否安全,还是我们正处于更快、更便宜、由 AI 驱动的审计的边缘?在 戛纳 EthCCZero Knowledge 播客 中深入研究之后,我们更进一步——我们构建了自己的实验性 AI 驱动的 ZK 审计工具 SnarkSentinel。在这篇文章中,我将分享哪些有效,哪些无效,以及当人类和 AI 团队合作或冲突时,审计的未来会是什么样子……

从想法到实现:Circom 作为核心

我们在 2023 年初成立了 zkSecurity,就在 ChatGPT 首次发布几个月后。我们印象深刻,但我们还没有掌握事情会发展得如此之快。两年过去了,我们现在拥有令人惊叹的 LLM,能够做各种令人惊叹的事情。

这让我们想知道……AI 会来取代我们吗?然后一个穿着皮夹克的家伙,看起来很像我们时代的先知,在某个会议上说道,这让我们难以忘怀:

“AI 不会抢走你的工作。使用 AI 的人会抢走你的工作。”——穿着皮夹克的家伙

游戏开始。我们决定成为问题的一部分,并开始试验我们自己的工具。

circom

我们的第一个目标是 Circom 代码。如果你不了解 Circom,它是一种用于编写 zk 电路的编程语言。我们选择它是因为我们已经在审计许多使用该框架构建的项目(例如,参见)。此外,Circom 项目通常是独立的,并且包含许多涉及密码学的底层算法。

如果我们相信 Anthropic 的最新报告(系统卡:Claude Opus 4 & Claude Sonnet 4),与密码学相关的挑战相比,大型语言模型(LLM)更容易解决涉及简单 Web 应用程序的 Capture The Flag(CTF)挑战。所以这里的想法是,如果我们能够首先解决这个难题,那么任何其他问题都应该很容易。

从朴素的提示到上下文工程

naive

我们的第一种方法非常朴素:我们坐下来快速编写了一个提示(字面上就是你在上面看到的那个),然后将所有重要的代码粘贴在它下面。

在上图中,单词以颜色编码显示,因为文本被 LLM 视为 tokens(每种颜色代表一个 token)。Tokens 是 LLM 的货币单位:你按 token 付费(并且输出 token 通常比输入 token 更昂贵)。这很重要,因为每个模型都有它可以摄取和产生的 token 总数的限制,即所谓的 上下文窗口

我们使用的第一个模型具有相当小的上下文窗口,我们经常达到限制(尽管 Circom 应用程序与你通常的代码库相比相对较小)。随着时间的推移,LLM 增加了它们的上下文窗口,我们现在通常可以访问至少 20 万个 token(有时甚至更多,例如 Gemini 1.5 pro 模型及其 200 万个上下文窗口!)

context window

我们很快意识到,即使我们有一个足够大的上下文窗口来包含我们粘贴到其中的所有内容,我们得到的结果也是不确定的。另一方面,如果我们把我们放入提示中的内容整理成只与我们已经知道存在的 bug 相关的内容,那么 LLM 有时会找到它!这表明太多的数据会混淆 LLM,但适量的数据可以帮助它集中注意力

context engineering

我们可以采取多种方法,第一种是 微调 模型,以继续在特定的代码库上训练它。但这成本高昂,必须为每个新的代码库或模型完成(而且,我们确实更换了模型来测试不同的方法或升级到最新的流行模型)。此外,除非你有大量数据,否则它不会发挥太多作用,而且正如我们意识到的那样,最先进的(SOTA)模型都能够很好地处理 Circom 代码(有一些我们稍后会讨论的细节)。

我们转向的下一个解决方案是 RAG,它代表 Retrieval Augmented Generation。RAG 的概念通常使用向量数据库来实现,该数据库可以摄取所有类型的文档(这里是我们的代码库内容)并提供 相似性搜索。(在底层,向量存储通过找出哪些向量最接近来工作,但这超出了本文的范围。)然后,人类 编排者 可以执行查询(例如,关于与我们尝试分析的函数相关的某些函数),并将结果插入到提示中。

但我们正在尝试 自动化 事情!所以在这一点上,我们从简单的生成转向使用 代理,这些代理可以自己从这些向量数据库中检索数据。也许现在是介绍代理概念的好时机,代理是一种在你(用户)和 LLM 之间来回交互的方式,以协同构建提示。这类似于与 AI 聊天,就像我们在日常与 ChatGPTLe Chat 等应用程序的交互中所习惯的那样。最终结果可以是结构化的输出,例如,一些带有某些预期字段的 JSON,描述了我们希望 LLM 最终生成什么。

agents

使用可以查询包含代码库块的向量存储的代理是一种很有希望的方法,直到我们意识到我们可以做得更好,而不仅仅是“接近我们正在寻找的东西”。毕竟,我们已经从 Circom 中获得了我们需要的所有信息。因此,我们 fork 了 Circom,并使其输出一些依赖关系图,我们可以使用该图来为我们的代理的查询提供服务。

助手:这个函数的实现是什么?

用户:是……,它也调用……,并被……调用

这种方法适用于 Circom,但不适用于一般的代码库。在后一种情况下,事实证明,让代理访问 catlsgrep 足以将该工具推广到所有类型的代码库。

在这一点上,让代理请求它需要的代码是减少我们插入到上下文窗口中的 token 数量的好方法。但有时它请求的不够,并开始产生幻觉。例如,它会幻想着某个函数返回一个你应该断言的布尔值,但断言实际上是在被调用的函数内部完成的,并且没有返回布尔值。通过一些 提示工程,我们可以强制它引用来自脚本的来源和片段(从而请求缺失的实现)。我们花了很多时间在调试跟踪上,以查看代理会在哪里出错,然后通过更好的提示来修复它。

另一方面,探索有时可能涉及太多无用的 catls 以及来自读取文件中不相关的内容。这是我们在 Web 搜索、文档搜索等中观察到的一个普遍问题。因此,我们经常尝试用它们自己的“专家代理”来包装工具。在这个例子中,我们创建了一个 代码库专家代理 来按需探索代码库并返回摘要结果。这成本很高,但很值得,因为上下文中的内容变得更加紧凑并且与提示相关。

fs agent

代理的混合和框架战争

在这一点上,我们正在使用安全代理背后的服务代理。但是安全代理本身只能处理这么多,所以我们也引入了一个概览代理,其主要目标是创建代码库的概览并找到较小的独立区域进行分析。对于这些较小的区域,我们将启动一个安全代理,该代理的任务是仅将自己限制在代码库的这些角落。这种 代理混合 方法是一种自然的演变,我们预见到下一次演变可能会看到更多的代理,可能还有一长串“微型代理”,它们将只关注特定类型的错误。最重要的是,我们重新开始使用 RAG,以允许人们向上下文中添加更多相关文档,而不会使其不堪重负,使用文档专家代理来包装向量存储。

micro-agents

为了编排这些复杂的 代理工作流程,我们查看了现有的框架列表。不幸的是,情况似乎类似于 2016 年的 javascript,有太多的框架可供选择。大多数框架也会在每次更新时破坏你的应用程序,因为 AI 概念仍在不断变化和快速发展。一些框架还具有严重偏见的抽象,使得难以推理底层组件。

frameworks

最终,我们决定使用 openAI agent SDK 作为主要框架,因为它提供了有趣的内置服务,同时保持足够低的级别(这应该允许我们相对容易地更换框架,如果某些框架最终成为代理框架的 React)。然后,当其他框架提供特定的工具(例如 llama-index 用于 RAG)时,我们仍然会使用它们。

上下文学习:自 2023 年以来拯救 Noob 模型

找到最佳的可用模型是改进工具的最简单方法之一。在我们寻找所有模型中的冠军的过程中,我们意识到大多数模型对 Circom 没有很好的理解,包括它的功能(例如,assert 是一个不安全的函数,因为它不会创建约束)和它的威胁模型(即,不应信任提示,因为证明者可以任意修改它们的逻辑)。

更糟糕的是,没有小型模型(包括 distilled 的 ~7B 参数的模型)甚至可以回忆起它们训练中的任何 circom 代码。

distilled

为了弄清楚不同的模型表现如何,我们使用了一个名为 LLM-as-a-judge 的技巧,让一个代理(知道答案)来判断另一个代理对我们棘手问题的回答。

结果令人惊讶:即使是 SOTA 模型也会回答错误,尽管有时会回答正确!为了解决这个问题,我们首先给模型一些指导,后来我们最终在上下文中添加了 Circom 的大部分文档,因为我们意识到我们使用的模型仍然对 Circom 的工作方式有无数的疑问(这从它们会进行的网络搜索中显而易见)。

models

虽然这表明微调可以在某个时候帮助我们,但这种 上下文学习 方法(而不是训练方法)在 SOTA 模型中效果非常好,尤其是在 推理模型 中。

投入测试:结果

实际结果如何?为了使用 SnarkSentinel 进行实验,我们在许多开源项目上运行了它,也在完成审计后立即在真实的审计项目上运行它,以便查看该工具是否可以找到我们实际发现的错误(以及可能更多的错误)。

该工具最终非常擅长发现 小的错误,这些错误是众所周知的,不一定是 Circom 特有的。例如,它发现了一些合法的 差一错误(这些错误不可利用)。我们还发现它擅长比较规范和实现(这通常是你作为审计员需要做的事情,并且可能是一个乏味的过程)。它还发现了 严重的 密码学错误,包括一个我们需要报告的错误(参见下一节)。

我们还得到了一些 假阴性几乎假阴性。一个发现指向了我们发现的一个严重错误,但代理错误地认为该错误的影响很小(我们假设它不能很好地理解其影响,并且这是一个缺乏上下文的问题),另一个发现指向了我们发现的一个严重错误,但代理说由于它直接从项目文档中引用了一个假设,实际上这不是一个可利用的错误。这一个很有趣,该项目的文档确实说 2048 位数字太难分解,错误地假设这样的数字不可能是平滑的。

对于我们发现的其他严重错误,它 通常指向它们的方向,但没有发现它们(而是发现了不相关的东西)。我们假设此时某些低级密码学错误太难找到,因为它们不常见,需要做的不仅仅是识别模式。尽管如此,正如之前指出的,我们现在正在研究专门调整的微型代理来查找这些类型的错误,看看我们是否能在某种程度上弥合差距。我们有一些想法,我们将在未来的更新中保留!

此外,我们正在考虑不同的技术,可以提高工具的整体结果。这一点很重要,因为似乎每次运行都会产生不同的错误,可能必须多次运行该工具才能找到特定的错误。这一点已被类似的研究多次证实,例如 这篇文章提到

o3 在基准测试中 100 次运行中有 8 次发现了 kerberos 身份验证漏洞

因此,我们目前还允许该工具多次运行,并使用一种 LLM-as-a-judge 的方法来判断新错误是否是先前发现的错误的重复项。此过程目前涉及代理向报告错误的向量存储进行查询,这有点慢,但可以改进。

false positives

在我们实验中,更大的问题是 假阳性。我们收到了大量的假阳性,这使得分类成为这项研究中最耗时的部分。我们认为,一个具有太多假阳性的工具对审计员来说不会有用,即使对于审计员来说,它也可能导致太多的摩擦,无法在审计期间使用。我们尝试要求代理生成概念验证,在某些情况下效果很好(特别是如果已经设置了测试框架),但是当漏洞利用涉及密码学时,该工具通常会被卡住(例如,试图生成具有特定属性的密钥对)。

无论如何,该工具仍然似乎是一个净收益,因为它允许对项目进行更详尽的覆盖,并给出好的想法和线索,即使它没有发现实际的错误。总的来说,似乎该工具需要大量的人工干预,从一开始收集更多的上下文,到调试跟踪的运行,但尤其是在最后提供专业知识来理解发现是否真的是发现。

案例研究:野外的一个严重漏洞

在一次运行中,SnarkSentinel 在我们正在审查的项目的一个超出范围的依赖项中发现了一个严重错误。(SnarkSentinel 的好处在于它甚至会检查项目的传递依赖项。) 更具体地说,SnarkSentinel 在 PSE 二进制 Merkle 根 库中发现了一个错误。

该库可用于计算二进制 Merkle 树的 Merkle 根,给定一个 leaf、一个 siblings 列表和一个 indices 数组。 这个 indices 数组应该由 01 值组成,但是该工具发现该电路中没有任何地方强制索引为布尔值,因此可以是任何字段元素。

在该库的实现中,来自 circomlibMultiMux1 模板用于选择要哈希的值(给定一个位选择器),以生成 Merkle 树所需的哈希根(通过 Poseidon 哈希函数):

var c[2][2] = [ [nodes[i], siblings[i]], [siblings[i], nodes[i]] ];
var childNodes[2] = MultiMux1(2)(c, indices[i]);

nodes[i + 1] <== Poseidon(2)(childNodes);

但是,查看 MultiMux1 模板,我们可以看到它只是计算两个常数的线性组合,假设给定的索引已经约束为 01

template MultiMux1(n) {
    signal input c[n][2];  // Constants
    signal input s;   // Selector
    signal output out[n];

    for (var i=0; i<n; i++) {

        out[i] <== (c[i][1] - c[i][0])*s + c[i][0];

    }
}

通过一些代数运算,任何人都可以找到索引值和同级值,使得 MultiMux1 模板的输出等效于一些诚实的叶对。 换句话说,攻击者可以制作恶意输入,使得任何 leaf 都可以被证明是 Merkle 树的一部分,即使它不是。

该工具无法正确评估此错误的影响,并且没有立即将其标记为严重。 但是,它给了我们足够的细节来指导我们朝着正确的方向前进,并让我们能够快速确认它。 这是一个很好的例子,说明 AI 如何帮助增强人工审计,因为它可以指出值得调查的代码区域,即使它没有发现错误本身。

在我们发现该错误后,我们立即联系了 PSE,他们已经意识到该问题,并在该库的 v2.0.0 版本中 修复 了该错误。

AI 审计的未来

future

很难预测接下来会发生什么。我们知道 AI 越来越好,但也开始了解它的局限性。目前,AI 只擅长人类擅长的事情,而且某件事越小众,就越难从中获得好的结果。

在目前的状态下,AI 最有可能不会取代我们对涉及高级密码学应用程序的严肃审计。但毫无疑问,它将取代低风险审计(例如,某些 Web 应用程序)和对良好理解的领域(例如,智能合约)的审计。我们认为许多咨询公司将会萎缩或倒闭。有趣的是,漏洞赏金主要是 AI 向 AI 代理报告错误,然后由它们进行分类,如果它们还不是这样的话。

我们正在问自己的另一个有趣的问题是“总的来说,我们会更安全还是更不安全?” 在我们看来,使用 AI 查找和利用错误将变得更加容易,可能会让技术含量较低的人(所谓的脚本小子)变得更加危险。与此同时,开发人员(如果他们也没有被取代)将获得相同的工具,并且能够更有效地保护他们的应用程序。

无论如何,有一点很清楚,作为审计员,我们也变得越来越危险。

  • 原文链接: blog.zksecurity.xyz/post...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zksecurity
zksecurity
Security audits, development, and research for ZKP, FHE, and MPC applications, and more generally advanced cryptography.