Cosmos 生态安全指南:解构 Cosmos 生态不同组件的安全场景
概况
Cosmos 提供了一种高效的跨链模式,特别适用于专注特定垂直领域的公链。通过提供模块化的区块链基础设施,Cosmos 为各应用开发者提供了便利,使其能够选择并使用符合其需求的公链。
背景
- CometBFT:跨链可扩展性的基石
- Cosmos SDK:模块化和新功能
- IBC 协议:增强互操作性和可扩展性
- CosmWasm:解锁去中心化、无许可部署
从链开发者的角度考虑,目前 Cosmos 上的生态链所需要大部分自定义化的功能都可以依赖 Cosmos SDK 来完成,而为了实现跨链操作过程中一些特殊操作或是出于优化性能等目的,链开发者会对各自 IBC 模块实现定制化,除此之外,也有少部分的链会对 CometBFT Core 等底层引擎做修改定制化,因篇幅限制暂时不在本研究报告中展开,本研究报告将重点剖析 Cosmos SDK 和 IBC 协议这两者的技术要点和安全问题。
Cosmos SDK 安全指南
Cosmos SDK 是一个强大而灵活的框架,用于构建区块链应用程序和去中心化协议。它由 Interchain 基金会开发,是 Cosmos 网络的核心组件,Cosmos 网络是互连区块链的去中心化网络。
在贯穿整个 Cosmos 生态的 ABCI 接口中,我们重点关注 BeginBlock、EndBlock、CheckTx、DeliverTx 这四个接口,前两者直接涉及到单个区块的执行逻辑,而后两者涉及到对 sdk.Msg(Cosmos 生态中传输消息的基础数据结构 ) 的具体处理。
由于 Cosmos 生态上各种应用链的实现逻辑都可按照类似 Cosmos SDK 里的模块和样例,因此在分析理解下文的安全漏洞时,需要对 Cosmos SDK 的模块运行流程有个基本概念。
在 Cosmos 生态里,交易最初在用户代理中被创建,然后会被签名并广播到区块链内的节点。节点利用 CheckTx 方法来验证各种交易细节,如签名、发件人的余额、交易序列和提供的燃料等。如果交易通过验证,它将被添加到内存池,等待被包含在一个区块中。另外,如果交易未通过验证,将向用户发送错误消息,导致交易被拒绝。在区块执行期间,会对交易进行进一步检查,这是通过 DeliverTx 方法完成的,以确保一致性和确定性。
以下是 Cosmos SDK 的具体执行逻辑,在分析下文漏洞触发路径时可以方便查阅理解:
Cosmos SDK 重点 ABCI 的具体执行逻辑
常见漏洞分类
在了解漏洞分类之前,我们需要对漏洞危险程度有一个基本划分,这样有助于更好地识别出危险性大的安全漏洞,尽可能规避潜在的安全风险。
1. 链停止运行
2. 资金损失
3. 影响系统状态或正常运行
而这些危险的起因往往是以下几种类型的安全漏洞:
1. 拒绝服务
2. 错误的状态设置
3. 验证缺失或者不合理
4. 唯一性问题
5. 共识算法问题
6. 实现上的逻辑漏洞
7. 语言特性问题
漏洞分析
链停止运行
链停止运行的罪魁祸首大多情况下是单个区块执行过程中产生的问题,但在 Cosmos 的历史发展过程中也出现过共识安全漏洞而导致链不得不主动停止来修复的情况,因此这里把影响共识类型的安全漏洞也放到导致链停止运行效果里来讨论,我们会分别来讲这两类问题。
第一类情况常见的是拒绝服务类型的漏洞,其具体原因主要是不当的崩溃处理与循环边界可被用户影响的遍历操作。此类漏洞往往会使得链暂停运行,或者减缓其运行速度等。
第二类情况是属于影响到共识的漏洞,通常和各类链的实现相关,目前已知的常见于一些逻辑处理验证,时间校准和随机性问题。此类漏洞从本质上会影响到区块链的去中心化原则,直观上看可能并没有太大影响,但假如经过有心者恶意设计,那么依然会产生较大的安全风险。
- 案例一:SuperNova 项目
漏洞背景:铸币分发操作中缺乏地址验证,从而导致操作失败,资金流失。在铸币模块中,每次 token 铸造都取决于抵押金额。然而,如果池不存在或者合约地址输入错误,铸币模块可能会发生意外情况,导致区块链停止运行。在奖励池模块中,池合约地址没有经过验证。如果分发操作失败,链将直接停止运行。
漏洞位置:https://github.com/Carina-labs/nova/blob/932b23ea391d4c89525c648e4103a3d6ee4531d5/x/mint/keeper/keeper.go#L175
漏洞代码片段:
漏洞触发路径:
BeginBlocker (/x/mint/keeper/abci.go)
Keeper.DistributeMintedCoin
Keeper.distributeLPIncentivePools
PoolIncentiveKeeper.GetAllIncentivePool (/x/mint/keeper/keeper.go)
漏洞补丁:
https://github.com/Carina-labs/nova/commit/538abc771dea68e33fd656031cbcf2b8fe006be0
补丁位于 poolincentive 的消息处理模块 (x/poolincentive/types/msgs.go),而非 mint 模块。
对处理 MsgCreateCandidatePool(即创建 pool)时的消息做了地址校验,是想从根源上杜绝错误地址的可能。
- 案例二:Cosmos Interchain Security 项目
项目地址:https://github.com/cosmos/interchain-security
漏洞背景:验证者可以通过在同一个区块中提交多个 AssignConsumerKey 消息来减缓提供链的速度。在 x/ccv/provider/keeper/key_assignment.go 中定义的 AssignConsumerKey 函数使验证者能够为经批准的消费链分配不同的 consumerKey。为了执行此操作,consumerAddrsToPrune AddressList 会增加一个元素。由于在 x/ccv/provider/keeper/relay.go 中的 EndBlocker 中会遍历这个 AddressList,攻击者可以利用它来减慢提供链的速度。为执行这种攻击,恶意行为者可以创建具有多个 AssignConsumerKey 消息的交易,并向提供链发送这些交易。consumerAddrsToPrune AddressList 的基数将与发送的 AssignConsumerKey 消息相同。因此,EndBlocker 的执行将花费比预期更多的时间和资源,导致提供链运行速度减缓,甚至停止。
漏洞位置:https://github.com/cosmos/interchain-security/blob/6a856d183cd6fc6f24e856e0080989ab53752102/x/ccv/provider/keeper/key_assignment.go#L378
漏洞代码片段:
漏洞触发路径:
msgServer.AssignConsumerKey
Keeper.AssignConsumerKey
AppModule.EndBlock
EndBlockCIS
HandleLeadingVSCMaturedPackets
HandleVSCMaturedPacket
PruneKeyAssignments
- 案例三:Quicksilver 项目 -Airdrop 模块
漏洞背景:BeginBlocker 和 EndBlocker 是模块开发人员可以在其模块中实现的可选方法。它们分别在每个区块的开始和结束时触发。在 BeginBlock 和 EndBlock 方法中使用崩溃来处理错误可能会导致链在出现错误时停止。EndBlocker 清算未完成的空投时未考虑模块是否有足够的代币从而触发崩溃的可能,导致链停止运行。
漏洞位置:https://github.com/quicksilver-zone/quicksilver/blob/b4aefa899e024d60f4047e2f2f403d67701b030e/x/airdrop/keeper/abci.go#L15
漏洞代码片段:
漏洞触发路径:
AppModule.EndBlock
Keeper.EndBlocker
Keeper.EndZoneDrop
漏洞补丁:https://github.com/quicksilver-zone/quicksilver/blob/20dc658480b1af1cb323b4ab4a8e5925ee79a0ed/x/airdrop/keeper/abci.go#L16
直接去掉了 panic 的处理代码,换成了记录错误日志。
- 案例四:Cosmos Interchain Security 项目
项目地址:https://github.com/cosmos/interchain-security
漏洞背景:攻击者可以通过向提供链的奖励地址发送多个代币来可以实现拒绝服务攻击。
在消费链的 EndBlocker 执行流程中,在 x/ccv/consumer/keeper/distribution.go 中定义的 SendRewardsToProvider 函数获取 tstProviderAddr 中所有代币的余额,并将它们发送到提供链。为了实现这一点,它必须遍历奖励地址中找到的所有代币,并逐个通过 IBC 发送到提供链。由于任何人都可以向奖励地址发送代币,攻击者可以创建并发送大量不同 denom 的代币,例如使用具有 token 工厂模块的链,来发起拒绝服务攻击。因此,EndBlocker 的执行将花费比预期更多的时间和资源,导致消费链运行速度减缓,甚至停止。
漏洞位置:https://github.com/cosmos/interchain-security/blob/6a856d183cd6fc6f24e856e0080989ab53752102/x/ccv/consumer/keeper/distribution.go#L103
漏洞代码片段:
漏洞触发路径:
AppModule.EndBlock
EndBlock
EndBlockRD
SendRewardsToProvider
这一类共识问题可能并不能带来直观的严重危害,但是从整个区块链本质原则和体系考虑,亦或是从具体的场景去看这些漏洞的话,它们带来的影响和危害并不见得比其他常规漏洞危害小,除此之外,这类漏洞也有存在着共同点。
- 案例一
漏洞背景:Cosmos SDK Security Advisory Jackfruit
Cosmos SDK 的 x/authz 模块中 ValidateBasic 方法的非确定性行为容易导致共识停止。x/authz 模块的 MsgGrant 消息结构包含一个 Grant 字段,其中包括用户定义的授权所授予的到期时间。在 Grant 结构的 ValidateBasic() 验证处理中,比较其时间信息与节点本地的时间信息而不是区块时间信息,由于本地时间是非确定性的,各个节点的时间戳可能存在差异,因此会导致共识问题。
漏洞公告:
https://forum.cosmos.network/t/cosmos-sdk-security-advisory-jackfruit/5319
https://forum.cosmos.network/t/cosmos-sdk-vulnerability-retrospective-security-advisory-jackfruit-october-12-2021/5349
https://github.com/cosmos/cosmos-sdk/security/advisories/GHSA-2p6r-37p9-89p2
漏洞代码片段:
漏洞补丁:
https://github.com/cosmos/cosmos-sdk/compare/v0.44.1...v0.44.2
像关于时间戳这样的问题不仅会出现在 Cosmos SDK 这样的基础组件中,在各类应用链中也曾出现过类似的漏洞。
- 案例二
漏洞背景:SuperNova,nova 项目
项目地址:https://github.com/Carina-labs/nova/tree/v0.6.3
使用 time.Now() 返回操作系统的时间戳。本地时间是主观的,因此是非确定性的。由于各个节点的时间戳可能存在小的差异,因此链可能无法达成共识。在 ICAControl 模块中,交易发送函数使用 time.Now() 来获取时间戳。
漏洞位置:https://github.com/Carina-labs/nova/blob/932b23ea391d4c89525c648e4103a3d6ee4531d5/x/icacontrol/keeper/send_msgs.go#L14
漏洞代码片段:
漏洞补丁:
将使用本地时间戳改为了使用区块时间。
timeoutTimestamp := time.Now().Add(time.Minute * 10).UnixNano() _, err = k.IcaControllerKeeper.SendTx(ctx, chanCap, connectionId, portID, packetData, uint64(timeoutTimestamp))
timeoutTimestamp := uint64(ctx.BlockTime().UnixNano() + 10*time.Minute.Nanoseconds()) _, err = k.IcaControllerKeeper.SendTx(ctx, chanCap, connectionId, portID, packetData, uint64(timeoutTimestamp))
- 案例三
漏洞背景:BandChain 项目的预言机模块
项目地址:https://github.com/bandprotocol/bandchain/
代码库中的注释表明,预言机模块必须在质押之前执行,以便实现违反预言机协议者的惩罚措施。代码中出现的顺序是:在 SetOrderEndBlockers 函数中,质押模块在预言机模块之前运行。如果质押模块在预言机模块之前执行,那么基于预言机模块中完成的验证来惩罚验证者质押等操作将是不可能实现的。
漏洞位置:https://github.com/LeastAuthority/bandchain/blob/master/chain/app/app.go#L221-L225
漏洞代码片段:
可以看到漏洞具体实现和漏洞注释完全是反过来的,预言机模块应该排在质押模块之前。
- 案例四
漏洞背景:Sei Cosmos 项目的 accesscontrol 模块
项目地址:https://github.com/sei-protocol/sei-cosmos
在 Cosmos 各类代码库的多个实例中,go map iteration 都有使用 go 语言的 map 类型,并对其进行了迭代。由于 go 语言的 map 的迭代是非确定性的,这将导致节点达到不同的状态,这可能会导致共识问题,从而导致链停止运行。比较合适的处理方法是将映射键排序到一个切片中,并迭代排序后的键。此类问题比较普遍,属于是语言特性在运用上引入的问题。
在 x/accesscontrol/keeper/keeper.go 的 BuildDependencyDag 的实现中,节点迭代 anteDepSet。由于 Go 中映射迭代的非确定性行为,ValidateAccessOp 可能会出现两种不同类型的错误,这可能会导致共识失败。
漏洞位置:https://github.com/sei-protocol/sei-cosmos/blob/afe957cab74dd05c213d082d50cae02dd4cb6b73/x/accesscontrol/keeper/keeper.go#L314C9-L314C9
漏洞代码片段:
资金损失
资金损失常见于 gas 消耗、资金被锁无法取出、资金传输过程中丢失、计算逻辑错误导致资金损失和资金存储 ID 未保证唯一性等逻辑情况。
这里我们以 SuperNova 项目为例,分析下和它相关的三个漏洞。
- 漏洞背景:SuperNova 项目
项目地址:https://github.com/Carina-labs/nova
如果区域(zone)的小数位数高于 18,资金可能会被锁定。在该项目代码中,区域的小数位数没有上限,可以超过 18 位。在 ClaimSnMesssage 中,当用户希望索取他们的份额代币时,使用了 ClaimShareToken。然而,如果区域的小数位数高于 18,那么转换将失败,无法从系统中提取资产。因此通过控制区域小数位数,可以直接触发交易崩溃失败。
漏洞位置:https://github.com/Carina-labs/nova/blob/v0.6.3/x/gal/keeper/claim.go#L167
漏洞代码片段:
漏洞触发路径:
msgServer.ClaimSnAsset
Keeper.ClaimShareToken
Keeper.ConvertWAssetToSnAssetDecimal
precisionMultiplier
- 漏洞背景:SuperNova 项目
项目地址:https://github.com/Carina-labs/nova/
区域的唯一性未经验证。在已注册的区域上,使用区域 ID 来检查基础代币(BaseDenom)的唯一性,每个区域的 BaseDenom 应该是唯一的,如果基础代币的值设置错误,将导致资金损失。该项目在 RegisterZone 中设置基础代币之前,并未确保 BaseDenom 在所有主区域中都是唯一的,否则会存在用户向另一含有同名 BaseDenom 的恶意 zone 存储资金的可能性,从而造成资金损失。
漏洞位置:https://github.com/Carina-labs/nova/blob/v0.6.3/x/icacontrol/keeper/msg_server.go#L99
漏洞代码片段:
漏洞补丁:多了 DenomDuplicateCheck 检查
除此之外上文链停止运行第一种情况中的案例一,是由于崩溃导致铸币失败,也是资金损失的一种形式。
- 漏洞背景:Supernova 项目
项目地址:https://github.com/Carina-labs/nova/
缺少状态更新。在 IcaWithdraw() 方法中,如果交易失败,版本状态没有修改,会导致 WithdrawRecord 无法访问,对应的资金也无法提取。更通俗的理解就是 state 设置在 SendTx 前,失败后未修改 state,导致该资金回撤失败并且下次无法再次回撤。
漏洞位置:https://github.com/Carina-labs/nova/blob/932b23ea391d4c89525c648e4103a3d6ee4531d5/x/gal/keeper/msg_server.go#L356
漏洞代码片段:
根据这部分的案例可以发现,与资金相关的操作主要实现逻辑,还是取决于对各类消息的处理,当然也存在如第一类情况的案例一在 BeginBlock 执行流程中涉及到的铸币操作,当这些操作失败时也会导致资金损失。整体来说,我们将审计重点放在涉及资金操作的代码模块就可以大幅提高发现此类漏洞的可能性。
影响系统状态或正常运行
sdk.Msg 类型中变量校验不完全
由于各项目基于 sdk.Msg 实现了各式各样的派生类型,但是在 Cosmos SDK 中其实并未对所有已有类型的各个元素做相应的检测,导致疏漏了一些关键的内嵌变量检测,从而存在一定的安全风险。
- 案例一:Cosmos SDK Security Advisory Barberry
漏洞背景:MsgCreatePeriodicVestingAccount.ValidateBasic 验证机制缺失对账户的存活等状态的判断。在 x/auth 中定义的 PeriodicVestingAccount,攻击者可以将受害者的账户初始化为恶意归属账户,该账户允许存款但不允许取款。当用户将资金存入其账户时,这些资金将被永久锁定,并且用户无法提取。
漏洞补丁:
https://forum.cosmos.network/t/cosmos-sdk-security-advisory-barberry/10825
https://github.com/cosmos/cosmos-sdk/security/advisories/GHSA-j2cr-jc39-wpx5
https://github.com/cosmos/cosmos-sdk/compare/v0.47.3-rc.0...v0.47.3
https://github.com/cosmos/cosmos-sdk/pull/16465
漏洞代码片段:
除此之外,Cosmos-SDK Security Advisory Elderflower、Cosmos-SDK Security Advisory Jackfruit,其实都是在 ValidateBasic 环节出现的问题,前者是直接缺失了对 ValidateBasic 的调用,后者则是在消息内部关于时间戳变量校验出现的问题。而在应用链,此类的问题更是常见,像 ethermint,pstake-native,quicksilver 等项目在处理消息的验证措施上都出现过类似的安全漏洞。
除了验证类型,在 sdk.Msg 的处理逻辑中,也会遇到如涉及到大量 gas 消耗的循环操作,不合理的崩溃处理等,由于针对消息的处理链上具备相应的 recovery 机制,因此它们的危险程度相对于链停止运行会低一些,但依然可以影响到系统的正常运行或是导致链上资金的损失。
常见类型漏洞
除去根据项目业务特有的安全漏洞,还存在一些比较常见的漏洞模型,比如资金损失案例三,就是一个在发送消息前改变状态的操作,这类漏洞和智能合约中的漏洞很相似,在传输资金之前先改变了自身的状态,往往会带来如重入或者遗留的错误状态等问题,像这种状态设置和消息传输紧密结合的场景在区块链中其实十分常见,很多造成重大危险的漏洞都是源于此类问题。除此之外,还有一些计算上的安全漏洞类似于除零漏洞、gas 消耗绕过、使用有漏洞的版本等,这类漏洞都会影响到系统状态或系统正常运行。
唯一性问题
由于区块链上涉及到大量的查找读取存储操作,因此在某些功能实现中,命名的唯一性非常重要。例如前文资金损失案例二,就是唯一性问题,除此之外一些代表 key 等重要因素的 string 或者 byte 数组类型的变量,有时候它们的前缀组成也会存在一定的风险,比如是否存在「/」作为每次命名的结尾等,稍有不慎就可能会导致命名被伪造成另一种含义的字符串,从而导致一系列如资金损失、共识出错等问题。
语言特性问题
这类问题更加宽泛一些,但是比较有特征可循,因此较为容易被发现,比如 golang 的 map 迭代问题、rust 中的一些 panic 机制问题等,建议在使用对应语言前,将这些语言特性本身可能会导致对应风险的点列举出来,在使用或者审计时单独关注即可最大程度上避免此类错误。
小结
根据我们对 Cosmos 生态底层安全问题的探索,其实不难发现,此类问题不仅适用于 Cosmos 生态,有很多漏洞模型同样可以运用在其他的生态链中,以下是有关研究 Cosmos 生态系统安全问题的一些建议和总结:
- 关注基础设施漏洞:CometBFT 和 Cosmos SDK 的核心组件也可能存在漏洞,因此需要对这些组件进行定期更新和维护,以确保安全性。
- 及时审查第三方库:Cosmos 开发者通常使用第三方库来扩展其应用程序的功能。然而,这些库可能包含潜在的漏洞,因此需要审查和更新这些库以降低风险。
- 注意恶意节点攻击:在 Cosmos 生态系统中,共识节点是网络的关键组成部分。节点的拜占庭容错算法可能会受到攻击,因此需要确保节点的安全性以防止不良行为。
- 注意物理安全:对于运行 Cosmos 节点的硬件和服务器,需要采取物理安全措施以防止未经授权的访问和潜在的攻击。
- 必要的代码审查:由于 Cosmos SDK 和 CometBFT 生态系统的开放性,开发者和审查员应该对核心代码以及在自定义模块中编写的代码进行审查,以识别和纠正潜在的安全问题。
- 留意生态系统工具:Cosmos 生态系统包括许多工具和应用程序,这些工具也需要进行安全审查和定期更新,以确保其安全性。
IBC 协议安全指南
自比特币问世以来,区块链领域经历了爆炸性的增长。数不清的新网络纷纷涌现,它们各自拥有独特的价值主张、共识机制、意识形态、支持者和存在理由。在引入 IBC 之前,这些区块链大都处于独立运作的状态,就像存在于封闭的容器中,无法相互通信,然而这种封闭的模式从根本上是不可持续的。
那么 IBC 是如何满足这些需求,并起到至关重要的作用呢?根本原因在于 IBC 是:
1. 无需信任的
2. 可支持异构区块链的
3. 可在应用层进行自定义的
4. 经过检验的成熟技术
IBC TAO:定义数据包的传输、身份验证和排序的标准,即基础设施层。在 ICS 中,这由核心、客户端和中继器类别组成。
IBC APP:定义通过传输层传递的数据包的应用程序处理程序的标准,这些包括但不限于同质化代币转账 (ICS-20)、非同质化代币转账 (ICS-721) 和链间账户 (ICS-27),并且可以在应用程序类别的 ICS 中找到。
IBC 协议是 Cosmos 坚持的区块链互联网(Internet of Blockchains)愿景的支柱。从这个意义上说,IBC 的设计选择受到了 TCP/IP 规范的影响。与 TCP/IP 为计算机通信制定标准的方式类似,IBC 也指定了一组通用抽象,通过将其实现可以允许区块链进行通信。TCP/IP 对通过网络中继的数据包的内容不设限制,IBC 也是如此。此外,与 HTTP 和 SMTP 等应用协议在 TCP/IP 上搭建的方式类似,同质化资产 / 非同质化资产传输或跨链智能合约调用等应用实例也将 IBC 协议作为基础层。
常见漏洞分类
由于篇幅限制,这里不再针对 IBC 协议中各个环节和组件做细致的分析,只对现有的一些安全漏洞做分类探讨,如果想了解更为细致和全面的分析,欢迎联系我们 CertiK 的安全工程师交流探讨。
1.命名漏洞
① 字符串处理漏洞
② 字节码处理漏洞
2. 传输过程漏洞
① 数据包顺序漏洞
② 数据包超时漏洞
③ 数据包认证漏洞
④ 其他数据包漏洞
3. 逻辑漏洞
① 状态更新漏洞
② 投票共识等漏洞
③ 其他逻辑漏洞
4. Gas 消耗漏洞
现有的 IBC 协议,在审计和分析其安全性的流程上和 Web2 协议的审计流程上具有较多相似性,这次我们将从协议的制定、实现和运用扩展这样一个完整流程的角度来剖析 IBC 协议上的部分安全问题和潜在风险。由于协议的制定往往是由少数人员和组织去完成的,对于各类区块链组织来说,更多的工作是围绕在协议的实现和运用扩展上,因此本文也将重点讨论这两者的安全问题。这是出于对 IBC 协议安全风险涵盖范围广的考虑,能更好地把协议上不同类型的安全问题划分到对应的环节和模块中。
漏洞分析
IBC 协议的制定
- 案例一:ICS-07 协议,逻辑漏洞
漏洞背景:解绑期限的错误使用
在代码中存在如下校验:
if currentTimestamp.Sub(consState.Timestamp) >= clientState.UnbondingPeriod {
根据 Tendermint 安全模型来看,时间为 t 的区块(标头)的 NextValidators 中的验证器需要在 t+TrustingPeriod 之前正确运行,在那之后,它们可能会有其他行为。然而这里检查的是 UnbondingPeriod,而不是 TrustingPeriod,其中 UnbondingPeriod>TrustingPeriod。如果 consState 的期限介于 TrustingPeriod 和 UnbondingPeriod 之间,那么将接受该标头作为用于验证构成不当行为的其中一个冲突标头的基准。在此期间,consState.NextValidators 中的验证器不再被认为是值得信任的,敌对的前验证者可以在不冒任何风险的情况下关闭客户端。
项目地址:https://github.com/cosmos/ibc/tree/e01da1d1346e578297148c9833ee4412e1b2f254/spec/ics-007-tendermint-client
漏洞位置:https://github.com/cosmos/ibc/tree/e01da1d1346e578297148c9833ee4412e1b2f254/spec/ics-007-tendermint-client#misbehaviour-predicate
https://github.com/cosmos/cosmos-sdk/blob/6344d626db1fbdba5e0f67425703c1584021bf5b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle.go#L96
漏洞代码片段:
IBC 协议的实现
IBC 协议的实现环节是比较容易出现问题的地方,因为这个环节起到了承上启下的作用,既要尽可能的避免协议规范中的歧义问题,又需要为了协议后续的运用和扩展实现提供更为基础便捷的接口。因此这里将 IBC 协议实现环节的主要问题再做一个小分类,即:
1. 协议实现中的歧义和不规范问题
2. 协议设定误差问题
- 案例一:ICS-20 协议,命名漏洞
漏洞背景:托管地址冲突。GetEscrowAddress() 是截断为 20 字节的 SHA256(端口 ID+ 通道 ID)。这种方法存在三个问题:
项目地址:https://github.com/cosmos/ibc/tree/e01da1d1346e578297148c9833ee4412e1b2f254/spec/ics-020-fungible-token-transfer
漏洞位置:https://github.com/cosmos/cosmos-sdk/blob/6cbbe0d4ef90f886dfc356979b89979ddfcd00d8/x/ibc/applications/transfer/types/keys.go#L40-L47
https://github.com/cosmos/cosmos-sdk/blob/6cbbe0d4ef90f886dfc356979b89979ddfcd00d8/x/ibc/applications/transfer/keeper/relay.go
漏洞代码片段:
协议设定误差问题
- 案例一:IBC Security Advisory Dragonberry,传输过程漏洞
漏洞背景:IBC 在处理应用数据包时会使用一个 Packet 的结构,根据超时机制,同步和异步的确认机制以及相应的证明验证流程,数据包会分成两个执行流程:
1.正常情况:超时内成功
2.超时情况:超时失败
当发生超时情况时,说明此次传输失败,IBC 协议便会发起退款流程,同时需要注意的是 IBC 具有用户可配置的超时机制。此次的 Dragonberry 漏洞源自 ICS-23(IBC),该漏洞的根本原因在于用户可以伪造验证流程中的不存在证明(即未收到数据包的虚假证明),从而绕过安全校验,伪造出「合理的」IBC 超时情况来欺骗 IBC 协议,导致中继器发送带有虚假证明的超时数据包,并可能会升级为 ICS-20 双花问题,其漏洞的具体触发流程可以看下图。
项目地址:https://github.com/cosmos/ibc-go/tree/00a680cda52690a4ba835bf37f53acc41c01bc7a/modules/core/04-channel
漏洞位置:https://github.com/cosmos/ibc-go/blob/00a680cda52690a4ba835bf37f53acc41c01bc7a/modules/core/04-channel/keeper/timeout.go#L117C28-L117C54
漏洞代码片段:
- 案例二:IBC Security Advisory Huckleberry,传输过程漏洞
漏洞背景:UnreceivedPackets 仅通过查找查询中包含的每个序列号的相应数据包收据来构建响应。这仅适用于无序通道,因为有序通道使用的是 nextSequenceRecv 而不是数据包收据。因此,在有序通道上,通过 GetPacketReceipt 查询序列号不会找到其中的收据。
此问题的严重程度较轻,因为 ICS-20 FT 传输的通道大部分是无序的,并且中继器不依赖这个 grpc 端点来确定触发超时数据包。然而,假如目标链中有大量数据包,同时配置了用于 IBC 传输的有序通道,且没有对 grpc 响应进行分页,这将产生导致服务节点性能下降甚至崩溃的风险。其具体的触发流程可以看下图。
项目地址:https://github.com/cosmos/ibc-go/blob/11297aaa61e651c16e6d4147a15be24ce55ba7cc/modules/core/04-channel/
漏洞位置:https://github.com/cosmos/ibc-go/blob/11297aaa61e651c16e6d4147a15be24ce55ba7cc/modules/core/04-channel/keeper/grpc_query.go#L408
漏洞代码片段:
IBC 协议的运用和扩展
- 案例一:stride airdrop 漏洞,逻辑漏洞
漏洞背景:TryUpdateAirdropClaim 该函数将 IBC 数据包的发送者地址转换为名为 senderStrideAddress 的 Stride 地址,并从数据包元数据中提取 airdropId 和新的空投地址 newStrideAddress。然后调用 UpdateAirdropAddress 来更新 senderStrideAddress 和 ClaimRecord。随着 ClaimRecord 的更新,newStrideAddress 将能够领取空投。然而这里更新函数只校验了 sender 该请求的地址是否为空,未对 newStrideAddress 做校验,由于 ibc 允许 solo machine 连接实现 IBC-enabled 的链,因此这里就存在更新任意其他账户地址作为空投地址的安全风险。
项目地址:https://github.com/Stride-Labs/stride/tree/3a5c7bfcc3b8c5e7dd870f01bebeb9d949492203/x/autopilot
漏洞位置:https://github.com/Stride-Labs/stride/blob/3a5c7bfcc3b8c5e7dd870f01bebeb9d949492203/x/autopilot/module_ibc.go#L119
https://github.com/Stride-Labs/stride/blob/3a5c7bfcc3b8c5e7dd870f01bebeb9d949492203/x/autopilot/keeper/airdrop.go#L17
漏洞代码片段:
- 案例二:neutron ibc 模块漏洞,gas 消耗漏洞
漏洞背景:智能合约为 IBC 事件的确认 / 超时支付的费用没有得到足够的验证,恶意智能合约可以利用这一点在 OnAcknowledgementPacket 和 OnTimeoutPacket 消息处理期间导致 ErrorOutOfGas 崩溃。这些崩溃不会通过 outOfGasRecovery 执行来恢复,这意味着交易不会包含在区块中,并且会导致 IBC 中继器重复提交消息,最终可能会造成中继器资金流失和网络废弃数据包过多的危害。
项目地址:https://github.com/neutron-org/neutron/blob/64868908b21f648ad5e8a9b48179134619544e2a/
漏洞位置:
https://github.com/neutron-org/neutron/blob/64868908b21f648ad5e8a9b48179134619544e2a/x/interchaintxs/keeper/ibc_handlers.go#L62
漏洞代码片段:
小结
除了上文提到 IBC 环节中的安全问题外,其他一些如 IBC 中间件的安全问题也在涌现,相信在未来,IBC 模块方面的安全问题会成为 Cosmos 生态安全中的重要一环。
总结
在针对 Cosmos 生态安全的探索与研究过程中,我们经历了复杂的审计、汇总和分类工作,探讨了关于 Cosmos 生态里最为关键的 Cosmos SDK 和 IBC 协议两者的安全问题,并通过丰富的实践经历,总结出大量通用的审计经验。
尽管如此,我们依然相信,通过类似本报告所总结的一些通用场景和安全问题,可以逐步提高异构网络 Cosmos 生态整体的安全性。