CertiK的Skyfall团队最近在Aptos、StarCoin和Sui等多个区块链中发现了基于Rust的RPC节点的多个漏洞。由于RPC节点是连接dApp和底层区块链的关键基础设施组件,其稳健性对于无缝操作至关重要。区块链设计者都知道稳定RPC服务的重要性,因此他们采用Rust等内存安全语言来规避可能破坏RPC节点的常见漏洞。
采用内存安全语言(如Rust)有助于RPC节点避免许多基于内存破坏漏洞的攻击。然而,通过最近的审计,我们发现即使是内存安全的Rust实现,如果没有经过精心设计和审查,也很容易受到某些安全威胁的影响,从而破坏RPC服务的可用性。
本文我们将通过实际案例介绍我们对一系列漏洞的发现。
区块链RPC节点作用
区块链的远程过程调用(RPC)服务是Layer 1区块链的核心基础设施组件。它为用户提供重要的API前端,并作为通向后端区块链网络的网关。然而,区块链RPC服务与传统的RPC服务不同,它方便用户交互无需身份验证。服务的持续可用性至关重要,任何服务中断都会严重影响底层区块链的可用性。
审计角度:传统RPC服务器 VS 区块链RPC服务器
对传统RPC服务器的审计主要集中在输入验证、授权/认证、跨站请求伪造/服务器端请求伪造(CSRF/SSRF)、注入漏洞(如SQL注入、命令注入)和信息泄露等方面进检查。
然而,区块链RPC服务器的情况有所不同。只要交易是签名的,就不需要在RPC层对发起请求的客户端进行身份验证。作为区块链的前端,RPC服务的一个主要目标是保证其可用性。如果它失效,用户就无法与区块链交互,从而阻碍查询链上数据、提交交易或发布合约功能。
因此,区块链RPC服务器最脆弱的方面是“可用性”。如果服务器宕机,用户就失去了与区块链交互的能力。更严重的是,一些攻击会在链上扩散,影响大量节点,甚至导致整个网络瘫痪。
为何新区块链会采用内存安全的RPC
一些著名的Layer 1区块链,如Aptos和Sui,使用内存安全编程语言Rust实现其RPC服务。得益于其强大的安全性和编译时严格的检查,Rust几乎可以使程序免受内存破坏漏洞的影响,如堆栈溢出、和空指针解引用和释放后重引用等漏洞。
英国央行副行长:FTX崩溃凸显出加强加密货币监管的迫切需求:11月23日消息,英国央行负责金融稳定副行长Jon Cunliffe爵士表示,加密货币交易所FTX的崩溃凸显出加强加密货币监管的迫切需求。Jon Cunliffe爵士说:“我们不应等到加密货币规模庞大且相互关联时才制定必要的监管框架,以防止可能产生更大破坏稳定影响的加密货币冲击。”
此外,Cunliffe补充说,英国财政部将很快开始就扩大投资者保护、市场诚信和其他监管框架进行磋商,这些框架涵盖金融产品的推广和交易,涉及涉及加密资产的活动和实体。[2022/11/23 8:00:38]
为了进一步确保代码库的安全,开发人员需严格遵循最佳实践,例如不引入不安全代码。在源代码中使用#![forbid(unsafe_code)]确保阻拦过滤不安全的代码。
区块链开发者执行Rust编程实践的例子
为了防止整数溢出,开发人员通常使用checked_add、checked_sub、saturating_add、saturating_sub等函数,而不是简单的加法和减法(+、-)。通过设置适当的超时、请求大小限制和请求项数限制来缓解资源耗尽。
Layer 1区块链中内存安全RPC威胁
尽管不存在传统意义上内存不安全的漏洞,但RPC节点会暴露在攻击者容易操纵的输入中。在内存安全RPC实现中,有几种情况会导致拒绝服务。例如,内存放大可能会耗尽服务的内存,而逻辑问题可能会引入无限循环。此外,竞态条件也可能构成威胁,并发操作可能会出现意外的事件序列,从而使系统处于未定义的状态。此外,管理不当的依赖关系和第三方库可能会给系统带来未知漏洞。
在这篇文章中,我们的目的是让人们关注可以触发 Rust 运行时保护的更直接的方式,从而导致服务自行中止。
显式的Rust Panic:一种直接终止RPC 服务的方法
Matthew?Roszak:即使在加密货币崩溃的情况下,DeFi仍然按计划工作:7月19日消息,区块链初创公司Bloq联合创始人Matthew?Roszak表示:即使在加密货币崩溃的情况下,DEFI(去中心化金融)仍然按计划工作。[2022/7/20 2:24:26]
开发人员可以有意或无意地引入显式panic代码。这些代码主要用于处理意外或异常情况。一些常见的例子包括:
assert!():当必须满足一个条件时使用该macro。如果断言的条件失败,程序将 panic,表明代码中存在严重错误。
panic!():当程序遇到无法恢复的错误且无法继续执行时调用该函数。
unreachable!():当一段代码不应该被执行时使用该macro。如果该macro被调用,则表示存在严重的逻辑错误。
unimplemented!()和todo!():这些宏是尚未实现功能的占位符。如果达到该值,程序将崩溃。
unwrap():该方法用于Option或Result类型,当遇到Err变量或None时会导致程序宕机。
漏洞一:触发Move Verifier中的assert!
Aptos区块链采用Move字节码验证器,通过对字节码的抽象解释进行引用安全分析。execute()函数是TransferFunctions trait实现的一部分,模拟基本块中字节码指令的执行。
函数execute_inner()的任务是解释当前字节码指令并相应地更新状态。如果我们已经执行到基本块中的最后一条指令,如index == last_index所示,函数将调用assert!(self.stack.is_empty())以确保栈为空。此行为背后的意图是保证所有操作都是平衡的,这也意味着每次入栈都有相应的出栈。
加密社区为庆祝“无聊猿”BAYC一岁生日导致Otherside Discord服务器短时崩溃:金色财经报道,“无聊猿”Bored Ape Yacht Club今日宣布于北京时间5月1日零时启动新项目Otherside,同时在其官方社交媒体上宣布庆祝诞生一周年。消息一出,大量 NFT 和“无聊猿”BAYC爱好者纷纷涌入Otherside Discord,结果导致Discord服务器出现短时崩溃,目前已恢复。[2022/4/24 14:45:27]
在正常的执行流程中,栈在抽象解释过程中始终是平衡的。堆栈平衡检查器(Stack Balance Checker)保证了这一点,它在解释之前对字节码进行了验证。然而,一旦我们将视角扩展到抽象解释器的范围,就会发现堆栈平衡假设并不总是有效的。
AbstractInterpreter中analyze_function漏洞的补丁程序
抽象解释器的核心是在基本块级别中模拟字节码。在其最初的实现中,在execute_block过程中,遇到错误会提示分析过程记录错误,并继续执行控制流图中的下一个块。这可能会造成一种情况:执行块中的错误会导致堆栈不平衡。如果在这种情况下继续执行,就会在堆栈不为空的情况下进行assert!检查,从而引发panic。
这就使得攻击者有机可趁。攻击者可通过在execute_block()中设计特定的字节码来触发错误,随后execute()有可能在堆栈不为空的情况下执行assert,从而导致assert检查失败。这将进一步导致panic并终止RPC服务,从而影响其可用性。
为防止出现这种情况,已实施的修复中,确保了在execute_block函数首次出现错误时会停止整个分析过程,进而避免了因错误导致堆栈不平衡而继续分析时可能发生的后续崩溃风险。这一修改消除了可能引起panic的情况,并有助于提高抽象解释器的健壮性和安全性。
观点:灰度比特币信托可能会成为下一个崩溃的比特币信托:黄金支持者、比特币反对人士Peter Schiff发推称,过去5天里,灰度旗下以太坊信托的价格暴跌了56%,比本月早些时候的高点低了65%。
灰度的比特币信托最近发行了大量新股,可能会成为下一个崩溃的比特币信托。GBTC有史以来第一次可以以低于资产净值的价格交易。[2020/6/27]
漏洞二:触发StarCoin中的panic!
Starcoin区块链有自己的Move实现分叉。在这个Move repo中,Struct类型的构造函数中存在一个panic! 如果提供的StructDefinition拥有 Native 字段信息,就会显式触发这个panic!。
规范化例程中初始化结构体的显式panic
这种潜在风险存在于重新发布模块的过程中。如果被发布的模块已经存在于数据存储中,则需要对现有模块和攻击者控制的输入模块进行模块规范化处理。在这个过程中,"normalized::Module::new "函数会从攻击者控制的输入模块中构建模块结构,从而触发 "panic!"。
规范化例程的前提条件
通过从客户端提交特制的有效载荷,可以触发该panic。因此,恶意行为者可以破坏RPC服务的可用性。
结构初始化panic补丁
Starcoin的补丁引入了一个新的行为来处理Native情况。现在,它不会引起panic,而是返回一个空的ec。这减少了用户提交数据引起panic的可能性。
声音 | 比特币安全专家:一旦再次发生金融危机,加密市场将率先崩溃:比特币安全专家Andreas Antonopoulos近期接受采访时表示,如果现在再次发升经济泡沫破裂的情况,将比2008年金融危机更加严重。2008年主要是涉及房地产领域,而现如今将远不止一个泡沫那样简单。Antonopoulos解释道:“它将以多种方式扩散到每一种金融资产,同样也会扩散到加密领域……你投资的房地产、债券、股票……所有这些都是过度膨胀的。”接着Antonopoulos补充说,不应该为了检验救生艇有多好而希望出现危机。他指出,人们没有意识到的一个因素是,当出现金融危机时,加密货币市场在一开始就会出现大规模崩溃:“它将严重崩溃的原因是,许多基于廉价资金的风险投资公司投资和个人私人投资将会枯竭。当人们害怕的时候,当出现那样的经济衰退的时候,他们就会撤回投资,同样也会撤回加密投资。”此外,Antonopoulos坚称,大规模衰退的第一个影响将是加密崩溃,因为市场的流动性将会枯竭。他补充称,这是一个“典型的影响,也是衰退的一个症状”。崩盘后,他表示有多种可能性,其中之一是比特币成为一种安全的避险资产。(AMBCrypto)[2020/1/5]
隐式 Rust Panic: 一种容易被忽视的终止RPC服务的方法
显式 panic 在源代码中很容易识别,而隐式panic则更可能被开发人员忽略。隐式panic通常发生在使用标准或第三方库提供的API时。开发人员需要彻底阅读和理解API文档,否则他们的Rust程序可能会意外停止。
BTreeMap 中的隐式 panic
让我们以Rust STD中的BTreeMap为例。BTreeMap是一种常用的数据结构,它以排序的二叉树形式组织键值对。BTreeMap提供了两种通过键值检索值的方法:get(&self, key: &Q)和index(&self, key: &Q)。
方法get(&self, key: &Q)使用键检索值并返回一个Option。Option可以是Some(&V),如果key存在,则返回值的引用,如果在BTreeMap中没有找到key,则返回None。
另一方面,index(&self, key: &Q)直接返回键对应的值的引用。然而,它有一个很大的风险:如果键不存在于BTreeMap中,它会触发隐式panic。如果处理不当,程序可能会意外崩溃,从而成为一个潜在漏洞。
事实上,index(&self, key: &Q)方法是std::ops::Index trait的底层实现。该特质为不可变上下文中的索引操作(即 container[index])提供了方便的语法糖。开发者可以直接使用 btree_map[key],调用 index(&self, key: &Q) 方法。然而,他们可能会忽略这样一个事实:如果找不到key,这种用法可能会触发panic,从而对程序的稳定性造成隐性威胁。
漏洞三:在Sui RPC中触发隐式panic
Sui模块发布例程允许用户通过RPC提交模块有效载荷。在将请求转发给后端验证网络进行字节码验证之前,RPC处理程序使用SuiCommand::Publish函数直接反汇编接收到的模块。
在这个反汇编过程中,提交模块中的code_unit部分被用来构建一个VMControlFlowGraph。该构建过程包括创建基本块,这些块存储在一个名为 "'blocks' "的BTreeMap中。该过程包括创建和操作该Map,在某些条件下,隐式panic会在这里触发。
下面是一段简化的代码:
创建VMControlFlowGraph时的隐式panic
在该代码中,通过遍历代码并为每个代码单元创建一个新的基本块来创建一个新的VMControlFlowGraph。基本块存储在一个名为block的BTreeMap中。
在对堆栈进行迭代的循环中,使用block[&block]对块图进行索引,堆栈已经用ENTRY_BLOCK_ID进行了初始化。这里的假设是,在block映射中至少存在一个ENTRY_BLOCK_ID。
然而,这一假设并不总是成立的。例如,如果提交的代码是空的,那么在“创建基本块”过程之后,“块映射”仍然是空的。当代码稍后尝试使用&blocks[&block].successors中的for succ遍历块映射时,如果未找到key,可能会引起隐式panic。这是因为blocks[&block]表达式本质上是对index()方法的调用,如前所述,如果键不存在于BTreeMap中,index()方法将导致panic。
拥有远程访问权限的攻击者可以通过提交带有空code_unit字段的畸形模块有效载荷来利用该函数的漏洞。这个简单的RPC请求会导致整个JSON-RPC进程崩溃。如果攻击者以最小的代价持续发送此类畸形有效载荷,就会导致服务持续中断。在区块链网络中,这意味着网络可能无法确认新的交易,从而导致拒绝服务(DoS)情况。网络功能和用户对系统的信任将受到严重影响。
Sui的修复:从RPC发布例程中移除反汇编功能
值得注意的是,Move Bytecode Verifier中的CodeUnitVerifier负责确保code_unit部分绝不为空。然而,操作顺序使RPC处理程序暴露于潜在的漏洞中。这是因为验证过程是在Validator节点上进行的,而该节点是在RPC处理输入模块之后的一个阶段。
针对这一问题,Sui通过移除模块发布RPC例程中的反汇编功能来解决该漏洞。这是防止RPC服务处理潜在危险、未经验证的字节码的有效方法。
此外,值得注意的是,与对象查询相关的其他RPC方法也包含反汇编功能,但它们不容易受到使用空代码单元的攻击。这是因为它们总是在查询和反汇编现有的已发布模块。已发布的模块必须已经过验证,因此,在构建VMControlFlowGraph时,非空代码单元的假设始终成立。
对开发人员的建议
在了解了显式和隐式panic对区块链中RPC服务稳定性的威胁后,开发人员必须掌握预防或降低这些风险的策略。这些策略可以降低服务意外中断的可能性,提高系统的弹性。因此CertiK的专家团队提出以下建议,并作为Rust编程的最佳实践为大家列出。
Rust Panic Abstraction: 尽可能考虑使用Rust的catch_unwind函数来捕获panic,并将其转换为错误信息。这可以防止整个程序崩溃,并允许开发人员以可控的方式处理错误。
谨慎使用API:隐式panic通常是由于滥用标准或第三方库提供的API而发生的。因此,充分理解API并学会适当处理潜在错误至关重要。开发人员要始终假设API可能会失效,并为这种情况做好准备。
适当的错误处理:使用Result和Option类型进行错误处理,而非求助于panic。前者提供了一种更可控的方式来处理错误和特殊情况。
添加文档和注释:确保代码文档齐全,并在关键部分(包括可能发生panic的部分)添加注释。这将帮助其他开发人员了解潜在风险并有效处理。
总结
基于Rust的RPC节点在Aptos、StarCoin和Sui等区块链系统中扮演着重要的角色。由于它们用于连接DApp和底层区块链,因此它们的可靠性对于区块链系统的平稳运行至关重要。尽管这些系统使用的是内存安全语言Rust,但仍然存在设计不当的风险。CertiK的研究团队通过现实世界中的例子探讨了这些风险,也足以证明了内存安全编程中需要谨慎和细致的设计。
CertiK中文社区
企业专栏
阅读更多
金色财经
金色荐读
Block unicorn
区块链骑士
金色财经 善欧巴
Foresight News
深潮TechFlow
我们会走向经济崩溃,还是会迎来 Meme 币以及无聊猿 NFT 等资产的复苏?经过近一年半的加息,就在昨天,美联储终于宣布停止加息.
1900/1/1 0:00:00看有朋友吐槽zkSync总是宕机,其实称“宕机”略微言过其辞了,准确说是“出块不稳定”。 本质上是,Sequencer提交的交易,最终Verified的时间不稳定,但用户在交互端感知并不明显,因.
1900/1/1 0:00:00Forkast News 6 月 6 日刊登 Safeheron 技术 VP Kane Wang 的专栏文章,探讨 Ledger Recover 陷入争议困局的背后原因.
1900/1/1 0:00:00作者:Hilary Allen,FINANCIAL TIMES;编译:松雪,金色财经在加密货币领域最大和最重要的市场美国,监管压力正在增加.
1900/1/1 0:00:00作者:金色财经cryptonaitive在美国监管机构对加密货币的严厉监管下,加密社区在这两周也许已经风声鹤唳过于FUD了.
1900/1/1 0:00:00原文作者:Ryan、gt、coucou 导师指导:Jademont、Elaine、Bill /img/202379223634/0.
1900/1/1 0:00:00