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崩溃的问题:金色财经报道,美国共和党众议员希尔表示,需要立法解决FTX崩溃的问题,美国证交会在ESG标准上走错了方向。[2022/12/8 21:30:07]
为了进一步确保代码库的安全,开发人员需严格遵循最佳实践,例如不引入不安全代码。在源代码中使用#![forbid(unsafe_code)]确保阻拦过滤不安全的代码。
区块链开发者执行Rust编程实践的例子
为了防止整数溢出,开发人员通常使用checked_add、checked_sub、saturating_add、saturating_sub等函数,而不是简单的加法和减法(+、-)。通过设置适当的超时、请求大小限制和请求项数限制来缓解资源耗尽。
Layer 1区块链中内存安全RPC威胁
尽管不存在传统意义上内存不安全的漏洞,但RPC节点会暴露在攻击者容易操纵的输入中。在内存安全RPC实现中,有几种情况会导致拒绝服务。例如,内存放大可能会耗尽服务的内存,而逻辑问题可能会引入无限循环。此外,竞态条件也可能构成威胁,并发操作可能会出现意外的事件序列,从而使系统处于未定义的状态。此外,管理不当的依赖关系和第三方库可能会给系统带来未知漏洞。
在这篇文章中,我们的目的是让人们关注可以触发 Rust 运行时保护的更直接的方式,从而导致服务自行中止。
显式的Rust Panic:一种直接终止RPC 服务的方法
开发人员可以有意或无意地引入显式panic代码。这些代码主要用于处理意外或异常情况。一些常见的例子包括:
assert!():当必须满足一个条件时使用该macro。如果断言的条件失败,程序将 panic,表明代码中存在严重错误。
panic!():当程序遇到无法恢复的错误且无法继续执行时调用该函数。
CNBC:加密货币崩溃表明美联储对抗通胀的工作“几乎完成”:金色财经报道,CNBC 经济学者、主持人Jim Cramer 表示,加密货币市场的快速下滑表明美联储在抑制通胀的艰苦斗争中取得了进展,他解释说:在通胀战争中,有一条战线是美联储取得的全面胜利,那就是打击金融投机,随着加密货币的牺牲,美联储的工作几乎完成了,但他们似乎还不知道。他们只是准备让人们失业,以明确通货膨胀已成为过去,加密货币的下跌是所有苦难的根源。[2022/7/1 1:43:38]
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())以确保栈为空。此行为背后的意图是保证所有操作都是平衡的,这也意味着每次入栈都有相应的出栈。
在正常的执行流程中,栈在抽象解释过程中始终是平衡的。堆栈平衡检查器(Stack Balance Checker)保证了这一点,它在解释之前对字节码进行了验证。然而,一旦我们将视角扩展到抽象解释器的范围,就会发现堆栈平衡假设并不总是有效的。
Genie创始人:Gem企图使Genie的网站崩溃并降低性能:5月2日消息,NFT聚合市场Genie创始人兼CEO Scott发推表示,Genie刚刚解决了长达一天的DDOS攻击。当OthersideMeta开始铸造时,就迎来了DDOS攻击。过去24小时,带有\"gem.xyz\"的URL向Genie发送了170万个请求,企图使Genie的网站崩溃并降低性能。[2022/5/2 2:44:59]
AbstractInterpreter中analyze_function漏洞的补丁程序
抽象解释器的核心是在基本块级别中模拟字节码。在其最初的实现中,在execute_block过程中,遇到错误会提示分析过程记录错误,并继续执行控制流图中的下一个块。这可能会造成一种情况:执行块中的错误会导致堆栈不平衡。如果在这种情况下继续执行,就会在堆栈不为空的情况下进行assert!检查,从而引发panic。
这就使得攻击者有机可趁。攻击者可通过在execute_block()中设计特定的字节码来触发错误,随后execute()有可能在堆栈不为空的情况下执行assert,从而导致assert检查失败。这将进一步导致panic并终止RPC服务,从而影响其可用性。
为防止出现这种情况,已实施的修复中,确保了在execute_block函数首次出现错误时会停止整个分析过程,进而避免了因错误导致堆栈不平衡而继续分析时可能发生的后续崩溃风险。这一修改消除了可能引起panic的情况,并有助于提高抽象解释器的健壮性和安全性。
漏洞二:触发StarCoin中的panic!
Starcoin区块链有自己的Move实现分叉。在这个Move repo中,Struct类型的构造函数中存在一个panic! 如果提供的StructDefinition拥有 Native 字段信息,就会显式触发这个panic!。
《比特币标准》作者:法币崩溃并非比特币成功的先决条件:黎巴嫩美国大学前经济学教授、《比特币标准》(The Bitcoin Standard)一书的作者Saifedean Ammous近期在接受采访时表示,恶性通货膨胀危机可能不是比特币被主流接受的必要前提:“恶性通货膨胀不一定与比特币升值有关。我认为比特币可以在没有恶性通货膨胀的情况下升值。”他还指出,“可能会出现与(比特币流行)完全无关的恶性通胀。”Ammous表示,如果比特币“继续升值,每天的全球交易量继续增加”,比特币的普及程度将会加快。(Cryptonews)[2020/6/21]
规范化例程中初始化结构体的显式panic
这种潜在风险存在于重新发布模块的过程中。如果被发布的模块已经存在于数据存储中,则需要对现有模块和攻击者控制的输入模块进行模块规范化处理。在这个过程中,"normalized::Module::new "函数会从攻击者控制的输入模块中构建模块结构,从而触发 "panic!"。
规范化例程的前提条件
通过从客户端提交特制的有效载荷,可以触发该panic。因此,恶意行为者可以破坏RPC服务的可用性。
结构初始化panic补丁
Starcoin的补丁引入了一个新的行为来处理Native情况。现在,它不会引起panic,而是返回一个空的ec。这减少了用户提交数据引起panic的可能性。
隐式 Rust Panic: 一种容易被忽视的终止RPC服务的方法
黄金支持者Peter Schiff:BTC进一步崩溃可能很快失去其所有价值:黄金支持者Peter Schiff发推文称,他对很快失去钱包中的比特币并不感到难过,他说,是否持有比特币并不重要,因为比特币正在迅速失去价值。加密投资银行Galaxy Digital首席执行官Mike Novogratz则仍然相信比特币的力量。他发推表示,BTC和加密货币一直与信心有关。尽管最近的价格暴跌,全球对主要传统资产的信心已经减弱,但它又回到了比特币。然而,许多人现在对比特币感到失望,他们说比特币已经失去了被称为避险资产的特权。(U.Today)[2020/3/13]
显式 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
6月9日,为期两天的「北京智源大会」在中关村国家自主创新示范区会议中心成功开幕。智源大会是智源研究院(也被称为中国OpenAI的最强中国AI研究院)主办的年度国际性人工智能高端专业交流活动,定位.
1900/1/1 0:00:00来源:decrypt.co;编译:区块链骑士周二,众议院金融服务委员会的共和党议员要求SEC(美国证券交易委员会)撤回其拟议的修改“交易所”定义的规则.
1900/1/1 0:00:00观察加密 VC 的最新投资可以帮助你看出市场走向和尽早发现新趋势。最近 VC 都在看好哪些赛道的哪些项目?本文将梳理 25 个在 5 月获得顶级加密 VC 投资的项目,帮助读者快速理解.
1900/1/1 0:00:00作者:Steven Ehrlich,forbes;编译:Kate, Marsbit美国证券交易委员会(Securities and Exchange Commission.
1900/1/1 0:00:00$FERC代币6月1日免费铸造,很快引起社区关注,价格最高时市值达到1500万美元。假如将铸造gas费视为成本、每张(1000个币)5U的话,FERC相当于上涨了300倍.
1900/1/1 0:00:00摘要本次会议,联储宣布维持利率不变(5%-5.25)。点阵图显示,联储官员的利率预期有所上移,暗指今年下半年还将加息50bp,小超市场预期.
1900/1/1 0:00:00