RPC节点的崩溃:关于内存安全区块链RPC节点的新漏洞分析

'RPC节点崩溃:内存安全区块链RPC节点新漏洞分析'

区块链RPC节点的安全威胁和预防措施

最近,CertiK的Skyfall团队在Aptos、StarCoin和Sui等多个区块链中发现了基于Rust的RPC节点的多个漏洞。由于RPC节点是连接dApp和底层区块链的关键基础设施组件,其稳健性对于无缝操作至关重要。在本文中,我们将通过实际案例介绍我们对一系列漏洞的发现。

区块链RPC节点的重要性

区块链的远程过程调用(RPC)服务是Layer 1区块链的核心基础设施组件。它为用户提供重要的API前端,并作为通向后端区块链网络的网关。然而,区块链RPC服务与传统的RPC服务不同,它方便用户交互无需身份验证。服务的持续可用性至关重要,任何服务中断都会严重影响底层区块链的可用性。

传统RPC服务器与区块链RPC服务器的审计角度

对传统RPC服务器的审计主要集中在输入验证、授权/认证、跨站请求伪造/服务器端请求伪造(CSRF/SSRF)、注入漏洞(如SQL注入、命令注入)和信息泄露等方面进检查。然而,区块链RPC服务器的情况有所不同。只要交易是签名的,就不需要在RPC层对发起请求的客户端进行身份验证。作为区块链的前端,RPC服务的一个主要目标是保证其可用性。如果它失效,用户就无法与区块链交互,从而阻碍查询链上数据、提交交易或发布合约功能。

因此,区块链RPC服务器最脆弱的方面是“可用性”。如果服务器宕机,用户就失去了与区块链交互的能力。更严重的是,一些攻击会在链上扩散,影响大量节点,甚至导致整个网络瘫痪。

为何新区块链会采用内存安全的RPC

一些著名的Layer 1区块链,如Aptos和Sui,使用内存安全编程语言Rust实现其RPC服务。得益于其强大的安全性和编译时严格的检查,Rust几乎可以使程序免受内存破坏漏洞的影响,如堆栈溢出、和空指针解引用和释放后重引用等漏洞。

为了进一步确保代码库的安全,开发人员需严格遵循最佳实践,例如不引入不安全代码。在源代码中使用#![forbid(unsafe_code)]确保阻拦过滤不安全的代码。

例如,在防止整数溢出方面,开发人员通常使用checked_add、checked_sub、saturating_add、saturating_sub等函数,而不是简单的加法和减法(+、-)。通过设置适当的超时、请求大小限制和请求项数限制来缓解资源耗尽。

内存安全RPC的威胁和漏洞案例

尽管不存在传统意义上内存不安全的漏洞,但RPC节点会暴露在攻击者容易操纵的输入中。在内存安全RPC实现中,有几种情况会导致拒绝服务。例如,内存放大可能会耗尽服务的内存,而逻辑问题可能会引入无限循环。此外,竞态条件也可能构成威胁,并发操作可能会出现意外的事件序列,从而使系统处于未定义的状态。此外,管理不当的依赖关系和第三方库可能会给系统带来未知漏洞。

我们通过实际案例来说明这些漏洞。首先,我们介绍了Aptos区块链中的一个漏洞。Aptos区块链采用Move字节码验证器,通过对字节码的抽象解释进行引用安全分析。在解释字节码的过程中,需要保证每次入栈都有相应的出栈。然而,在某些情况下,错误的执行流程可能导致堆栈不平衡,进而触发拒绝服务。

针对这一问题,已实施的修复确保了在执行过程中发现错误时会停止整个分析过程,避免了可能导致堆栈不平衡的后续崩溃风险。这一修改消除了可能引起拒绝服务的情况,并提高了抽象解释器的健壮性和安全性。

接下来,我们介绍了StarCoin区块链中的一个漏洞。Starcoin区块链在其Struct类型的构造函数中存在一个LianGuainic! 如果提供的StructDefinition拥有Native字段信息,就会显式触发这个LianGuainic!。

在这种情况下,攻击者可以通过提交特制的有效载荷来触发该LianGuainic,从而导致RPC服务的拒绝服务。为解决这个问题,Starcoin的修复引入了一个新的行为来处理Native情况,不会引起LianGuainic,而是返回一个空的ec,从而降低了攻击者触发LianGuainic的可能性。

最后,我们介绍了Sui区块链中的一个漏洞。Sui模块发布例程允许用户通过RPC提交模块有效载荷。在反汇编这个有效载荷的过程中,构建VMControlFlowGraph的过程中可能会触发隐式LianGuainic。通过提交带有空code_unit字段的畸形模块有效载荷,攻击者可以利用该漏洞破坏RPC服务的可用性。

为解决这个问题,Sui通过移除模块发布RPC例程中的反汇编功能来修复该漏洞,从而防止RPC服务处理潜在危险、未经验证的字节码。

Rust中的显式和隐式LianGuainic

在Rust编程中,开发人员可以有意或无意地引入显式或隐式的LianGuainic代码。显式的LianGuainic代码主要用于处理意外或异常情况,如assert!()、LianGuainic!()等。而隐式的LianGuainic则更可能被开发人员忽略,通常发生在使用标准或第三方库提供的API时。

一个常见的例子是使用Rust的BTreeMap数据结构。在BTreeMap中,使用index()方法可以直接返回键对应的值的引用。然而,如果键不存在于BTreeMap中,这种用法可能会触发隐式LianGuainic,从而导致程序崩溃。

为避免这种情况,开发人员应该谨慎使用API,充分理解API并学会适当处理潜在错误。使用Result和Option类型进行错误处理,而非求助于LianGuainic,是一种更可控的方式。

预防措施和开发人员建议

在了解了显式和隐式LianGuainic对区块链中RPC服务稳定性的威胁后,开发人员必须掌握预防或降低这些风险的策略。以下是CertiK的专家团队提出的建议和Rust编程的最佳实践:

  1. 使用Rust的catch_unwind函数来捕获LianGuainic,并将其转换为错误信息,从而防止整个程序崩溃。
  2. 谨慎使用API,充分理解API并学会适当处理潜在错误。
  3. 使用Result和Option类型进行错误处理,而非求助于LianGuainic。
  4. 添加文档和注释,确保代码文档齐全,并在关键部分添加注释,帮助其他开发人员了解潜在风险并有效处理。

总结

基于Rust的RPC节点在Aptos、StarCoin和Sui等区块链系统中扮演着重要的角色。由于它们用于连接DApp和底层区块链,因此它们的可靠性对于区块链系统的平稳运行至关重要。尽管这些系统使用的是内存安全语言Rust,但仍然存在设计不当的风险。CertiK的研究团队通过现实世界中的例子探讨了这些风险,也足以证明了内存安全编程中需要谨慎和细致的设计。