深度分析 Sui 公链最新漏洞「仓鼠滚轮」为何被评为严重级?

就在上周一,CertiK 因发现重大安全漏洞 「仓鼠滚轮」,获得了 SUI 50 万美元漏洞赏金。下文中将从技术层面披露此关键漏洞的细节,阐明该漏洞的根本原因和潜在影响。
(前情提要:Sui公布「第二版」代币释放计画,附注:不保证内容、更改不会通知)
(背景补充:Sui基金会锁仓代币竟在质押?KOL痛骂内幕「挖提卖」,造成SUI疯狂通膨)

本文目录

此前,CertiK 团队於 Sui 区块链发现了一系列拒绝服务漏洞。在这些漏洞中,一种新型且具有严重影响力的漏洞格外引人注目。该漏洞可导致 Sui 网路节点无法处理新的交易,效果等同於整个网路完全关闭。

就在上周一,CertiK 因发现该重大安全漏洞,获得了 SUI 50 万美元漏洞赏金。美国业内权威媒体 CoinDesk 对该事件进行了报导,随後各大媒体也紧随其报导发布了相关新闻。

该安全漏洞被形象地称为 「仓鼠滚轮」:其独特的攻击方式与目前已知的攻击不同,攻击者只需提交一个大约 100 位元组的载荷,就能触发 Sui 验证节点中的一个无限回圈,使其不能响应新的交易。

此外,攻击带来的损害在网路重启後仍能持续,并且能在 Sui 网路中自动传播,让所有节点如仓鼠在轮上无休止地奔跑一样无法处理新的交易。因此我们将这种独特的攻击型别称为 「仓鼠滚轮」 攻击。

发现该漏洞後,CertiK 通过 Sui 的漏洞赏金计划向 Sui 进行了报告。Sui 也第一时间进行了有效回应,确认了该漏洞的严重性,并在主网启动前积极采取了相应措施对问题进行了修复。除了修复此特定的漏洞外,Sui 还实施了预防性的缓解措施,以减少该漏洞可能造成的潜在损害。

为了感谢 CertiK 团队负责地披露,Sui 向 CertiK 团队颁发了 50 万美元奖金。

下文中将从技术层面披露此关键漏洞的细节,阐明该漏洞的根本原因和潜在影响。

延伸阅读:SUI 遭爆3月秘密修复「十亿美元安全漏洞」,可让骇客闪电贷攻击..

漏洞详解验证器在 Sui 中的关键作用

如 Sui 和 Aptos 这样基於 Move 语言的区块链,其防止恶意载荷攻击的保障机制主要是静态验证技术。通过静态验证技术,Sui 可在合约发布或升级之前检查使用者提交的载荷有效性。验证器提供了一系列检查器用来确保结构和语义的正确性,只有当通过检查验证後,合约才会进入 Move 虚拟机器被执行。

Move 链上的恶意载荷威胁

Sui 链在原始 Move 虚拟机器之上提供了一套新的储存模型与介面,因此 Sui 有一个定制版的 Move 虚拟机器。为了支援新的储存原语,Sui 进一步针对不可信载荷的安全验证引入了一系列额外的、定制的检查手段,如物件安全及全域性储存访问等功能。这些定制检查手段契合了 Sui 的独特功能,因此我们将这些定制检查称为 Sui 验证器。

Sui 对载荷的检查顺序

如上图所示,验证器中的大多数检查会针对 CompiledModule(表示使用者提供的合约载荷执行)进行结构层面的安全验证。例如,通过 「重复检查器」 确保执行时载荷中没有重复的条目;通过 「限制检查器」 确保执行时载荷中每个栏位的长度都在允许的条目上限之内。

除了结构层面的检查之外,验证器的静态检查仍需要更复杂的分析手段,以确保不可信载荷在语义层面的强健性。

延伸阅读:Beosin:Move VM先前毁灭级漏洞,可让Sui、Aptos「崩溃、甚至硬分叉」

了解 Move 的抽象直译器:线性和迭代分析

由 Move 提供的抽象直译器,是一个专门为通过抽象解释在位元组码上执行复杂安全分析而设计的框架。这种机制使得验证过程更加精细和准确,每个验证者都被允许定义他们独特的抽象状态从而进行分析。

在开始执行时,抽象直译器从编译的模组中构建控制流图(CFG)。这些 CFG 中的每个基本块都会维护一组状态,即 「前序状态」 和 「後序状态」。「前序状态」 提供了一个基本块执行前的程式状态快照,而 「後序状态」 则提供了基本块执行後的程式状态描述。

当抽象直译器在控制流图中没有遇到回跳(或回圈)时,它则遵循一个简单的线性执行原则:每个基本块都被依次分析,并根据块中每个指令的语义计算出前序状态和後序状态。其结果就是一个程式在执行过程中每个基本块级别状态的精准快照,帮助验证程式的安全属性。

Move 抽象直译器的工作流程

然而,当控制流中存在回圈时,这个过程则变得更加复杂。回圈的出现意味着控制流图中包含一条回跳的边,回跳边的源头对应着当前基本块的後序状态,而回跳边的目标基本块(回圈顶级)是一个之前已经分析过的基本块的前序状态,因此抽象直译器需要对回跳相关的两个基本块的状态进行仔细合并。

如果发现合并後状态与回圈顶级基本块现有的前序状态不同,抽象直译器就会更新回圈顶级基本块的状态,并从这个基本块开始重新启动分析。这个迭代分析过程将一直持续到回圈预状态稳定。换句话说,这个过程不断重复,直到回圈顶级基本块的前序状态在迭代之间不再变化。达到一个固定点,则表明回圈分析已经完成。

Sui IDLeak 验证器:定制的抽象解释分析

与原来的 Move 设计不同,Sui 的区块链平台引入了一个独特的以 「目标」 为中心的全域性储存模型。这个模型的一个显着特点是:任何具有 key 属性(作为索引上链储存)的资料结构必须以 ID 型别作为该结构的第一个栏位。ID 栏位不可改变,且不能转移到其他目标上,因为每个物件必须有一个全域性唯一的 ID。为了确保这些特性,Sui 在抽象直译器上建立了一套自定义分析逻辑。

IDLeak 验证器,也被称为 id_leak_verifier,与抽象直译器协同工作进行分析。它有着自己独特的 AbstractDomain,被称为 AbstractState。每个 AbstractState 由多个区域性变数对应的 AbstractValue 组成。通过 AbstractValue 来监督每个区域性变数的状态,以此来追踪一个 ID 变数是否是全新的。

在结构体打包的过程中,IDLeak 验证器只允许将一个全新的 ID 打包到一个结构体中。通过抽象解释分析,IDLeak 验证器可以详尽地跟踪本地资料流状态,以确保没有现有的 ID 被转移到其他结构体物件。

Sui IDLeak 验证器状态维护不一致问题

IDLeak 验证器通过实现 AbstractState::join 函式与 Move 抽象直译器整合。这个函式在状态管理,特别是在合并和更新状态值方面中起着不可或缺的作用。

详细检查这些函式以了解它们的操作:

在 AbstractState::join 中,该函式将另一个 AbstractState 作为输入,并试图将其本地状态与当前物件的本地状态合并。对於输入状态中的每个区域性变数,它将该变数的值与它在区域性状态中的当前值进行比较(如果没有找到,预设值为 AbstractValue::Other)。如果这两个值不相等,它将设定一个 「changed」 的标志,作为最终状态合并结果是否变化的依据,并通过呼叫 AbstractValue::join 来更新本地状态中的本地变数值。

在 AbstractValue::join 中,该函式将其值与另一个 AbstractValue 进行比较。如果它们相等,它将返回传入的值。如果不相等,则返回 AbstractValue::Other。

然而,这个状态维护逻辑包含一个隐藏的不一致性问题。尽管 AbstractState::join 会基於新旧值的不同而返回一个表示合并状态发生变化(JoinResult::Changed)的结果,但合并更新後的状态值仍然可能是不变的。

这种不一致的问题是由操作顺序导致的:在 AbstractState::join 中对改变状态的判定发生在状态更新(AbstractValue::join)之前,这种判定并不反应真正的状态更新结果。

此外,在 AbstractValue::join 中,AbstractValue::Other 对合并的结果起着决定性作用。例如,如果旧值是 AbstractValue::Other,而新值是 AbstractValue::Fresh,则更新的状态值仍然是 AbstractValue::Other,即便新旧值不同,更新後状态本身没有变化。

示例:状态连线的不连贯性

这就引入了一个不一致:即合并基本块状态的结果被判定为 「改变」,但合并後的状态值本身并没有发生变化。在抽象解释分析的过程中,出现这种不一致问题有可能产生严重的後果。我们回顾抽象直译器在控制流图(CFG)中出现回圈时的行为:

当遇到一个回圈时,抽象直译器采用一种迭代的分析方法来合并回跳目标基本块和当前基本块的状态。如果合并後的状态发生变化,抽象直译器则会从跳转目标开始重新分析。

然而,如果抽象解释分析的合并操作错误地将状态合并结果标记为 「变化」,而实际上状态内部变数的值没有发生变化,就会导致无休止的重新分析,产生无限回圈。

延伸阅读:科普 | 以太坊EIP-4337「帐户抽象(AA)」是什麽?往後钱包无注记词!

进一步利用不一致在 Sui IDLeak 验证器中触发无限回圈

利用这种不一致性,攻击者可以构造一个恶意的控制流图,诱使 IDLeak 验证器进入一个无限回圈。这个精心构造的控制流图由三个基本块组成:BB1 和 BB2,BB3。值得注意的是,我们有意引入了一条从 BB3 到 BB2 的回跳边来构造一个回圈。

恶意 CFG + 状态,可导致 IDLeak 验证器内部死回圈

这个过程从 BB2 开始,其中一个特定区域性变数的 AbstractValue 被设定为::Other。在执行 BB2 之後,流程转移到 BB3,在那里同一变数被设定为::Fresh。在 BB3 的结尾处,有一条回跳边,跳转到 BB2。

在抽象解释分析这个例子的过程中,前文提到的不一致性起到了关键作用。当回跳边被处理时,抽象直译器试图将 BB3 的後序状态(变数为 「::Fresh」)与 BB2 的前序状态(变数为 「::Other」)连线起来。AbstractState::join 函式注意到了这个新旧值不同的差异并设定了 「变化」 的标志,以此表示需要对 BB2 的进行重新分析。

然而,AbstractValue::join 中 「::Other」 的主导行为意味着 AbstractValue 合并後,BB2 状态变数的实际值仍然是 「::Other」,状态合并的结果并没有发生变化。

因此这个回圈过程一旦开始,即当验证器继续重新分析 BB2 以及它的所有後继基本块节点(本例中为 BB3),它就会无限期地持续下去。无限回圈消耗了所有可用的 CPU 周期,使其无法处理响应新的交易,这种情况在验证器重新启动後仍然存在。

通过利用这个漏洞,验证节点如仓鼠在轮上无休止地奔跑一样无限回圈,无法处理新的交易。因此我们将这种独特的攻击型别称为 「仓鼠滚轮」 攻击。

「仓鼠滚轮」 攻击可以有效地使 Sui 验证器陷入停顿,进而导致整个 Sui 网路瘫痪。

理解了漏洞成因与触发过程之後,我们通过使用以下 Move 位元组码模拟构建了一个具体例子,成功地在真实环境中的模拟中触发了该漏洞:

这个例子通过精心构造的位元组码,展示了如何在真实环境中触发漏洞。具体来说,攻击者可以在 IDLeak 验证器中触发一个无限回圈,利用仅仅约 100 位元组的载荷即可消耗 Sui 节点的所有 CPU 周期,有效阻止新交易处理,并导致 Sui 网路拒绝服务。

延伸阅读:Sui台湾社群盛大同聚!Mysten Labs「核心谈话」聚焦Web3生态前景

「仓鼠滚轮」 攻击在 Sui 网路中的持续性危害

Sui 的漏洞赏金计划对漏洞等级的评定有着严格的规定,主要依据对整个网路的危害程度进行评定。满足 「严重(critical)」 评级的漏洞必须使整个网路关停并有效阻碍新交易确认,同时需要硬分叉来修复问题;如果漏洞只能使部分网路节点拒绝服务,至多被评定为 「中危(medium)」 或 「高危(high)」 漏洞。

CertiK Skyfall 团队发现的 「仓鼠滚轮」 漏洞可以使整个 Sui 网路关停,同时需要官方发布新版本进行升级修复。基於对该漏洞的危害程度,Sui 最终被将其评定为 「严重」 等级。为了进一步理解 「仓鼠滚轮」 攻击造成的严重性影响原因,我们有必要了解 Sui 後端系统的复杂架构,特别是链上交易发布或升级的整个过程。

在 Sui 中提交交易的互动概述

最初,使用者交易通过前端 RPC 提交,经基本验证後传递到後端服务。Sui 後端服务负责进一步验证传入的交易载荷。在成功验证了使用者的签名後,交易被转化为交易证书(包含交易资讯以及 Sui 的签名)。

这些交易证书是 Sui 网路运作的基本组成部分,可以在在网路中的各个验证节点之间传播。对於合约建立 / 升级交易,在其可以上链之前,验证节点会呼叫 Sui 验证器检查并验证这些证书的合约结构 / 语义的有效性。正是在这个关键的验证阶段,「死回圈」 漏洞可以被触发利用。

当该漏洞被触发时,它会导致验证过程无限期中断,有效阻碍系统处理新交易的能力,并导致网路完全关闭。雪上加霜的是,节点重启後该情况仍然存在,这也就意味着传统的缓解措施远远不够。该漏洞一旦被触发,则会出现 「持续破坏」 的情况从而对整个 Sui 网路留下持久影响。

Sui 的解决方法

经过 CertiK 回馈後,Sui 及时确认了该漏洞,并发布了一个修复程式来解决该关键缺陷。该修复程式确保了状态改变和改变後标志之间的一致性,消除了 「仓鼠滚轮」 攻击造成的关键影响。

为了消除上述的不一致,Sui 的修复包括对 AbstractState::join 函式的一个微小但关键的调整。这个更新移除了在执行 AbstractValue::join 之前判定状态合并结果的逻辑,取而代之的是首先执行 AbstractValue::join 函式进行状态合并,通过比较最终更新结果和原始状态值(old_value)来设定合并是否发生变化的标记。

这样一来,状态合并的结果与真实更新的结果将保持一致,分析过程中不会发生死回圈。

除了修复这个特定的漏洞外,Sui 还部署了缓解措施,以减少未来验证器漏洞的影响。根据 Sui 在 bug 报告中的回复,缓解措施涉及一个叫做 Denylist 的功能。

「然而,验证器有一个节点配置档案,允许他们暂时拒绝某些类别的交易。这个配置可以用来暂时禁止处理发布和软体包升级。由於这个 bug 是在签署发布或软体包升级 tx 之前执行 Sui 验证器时发生的,而拒绝列表将停止验证器的执行并将恶意 tx 丢弃,暂时拒绝列表这些 tx 型别是一个 100% 有效的缓解措施(尽管它将暂时中断试图发布或升级程式码的人的服务)。

顺便提一下,我们有这个 TX 拒绝列表配置档案已经有一段时间了,但我们也为证书添加了一个类似的机制,作为你之前报告的 「验证器死回圈」 漏洞的後续缓解手段。有了这个机制,我们将对这种攻击有更大的灵活性:我们将使用证书拒绝名单配置来使验证器忘记坏的证书(打破死回圈),并使用 TX 拒绝名单配置来禁止发布 / 升级,从而防止建立新的恶意攻击交易。谢谢你让我们思考这个问题!

验证器在签署交易之前有有限的 “ticks”(与 gas 不同)用於位元组码验证,如果在交易中发布的所有位元组码不能在这麽多 ticks 中得到验证,验证器将拒绝签署该交易,防止它在网路上执行。以前,计量只适用於一组选定的复杂验证器通过。为了应对这个问题,我们将计量扩充套件到每个验证器,以保证验证器在每个 tick 的验证过程中所执行的工作有一个约束。我们还修复了 ID 泄漏验证器中的潜在无限回圈错误。

— 来自 Sui 开发者关於漏洞修复的说明

总而言之,Denylist 使验证者能够通过禁用发布或升级流程来暂时规避针对验证器中的漏洞利用并有效地防止一些恶意交易带来的的潜在破坏。当 Denylist 的缓解措施生效时,节点通过牺牲自身的发布 / 更新合约功能,来确保自己能够继续工作。

总结

本文我们分享了由 CertiK Skyfall 团队发现的 「仓鼠滚轮」 攻击技术细节,解释了这种新型攻击是如何利用关键漏洞来导致 Sui 网路完全关闭的。此外,我们也仔细研究了 Sui 为修复这一关键问题所做的及时反应,并分享了漏洞修复以及後续同类漏洞缓解的方法。

📍相关报导📍

对话Mysten Labs共同创办人兼密码学家:Sui引入哪些密码学创新?

Sui如何解决以太坊痛点,达到「链上真自由」?专访 Mysten Labs 执行长 Evan Cheng

下一次 L1 之战中,哪些项目值得重点关注?Sui、Aptos、Aleo、Celestia、Monad…

Leave a Reply

Your email address will not be published. Required fields are marked *