该漏洞存在于Cosmos IBC协议的Transfer v2通道升级机制中。一方面,升级功能包含特定的HasInflightPackets
检查,它会查找通道的已提交包,如果存在任何未完成的包,就不会将通道设置为FLUSHCOMPLETE
状态。这在handleFlushState
、WriteUpgradeAckChannel
和WriteUpgradeConfirmChannel
函数中实现。如果通道不处于FLUSHCOMPLETE
状态,升级就无法完全完成,在ChanUpgradeOpen
中会报错,阻止进入WriteUpgradeOpenChannel
函数(该函数会将通道重新设置为开放状态并更新升级字段)。
包承诺可以通过两种方式删除:确认(acknowledgement)和超时(timeout)。
另一方面,转发功能在收到转发目标通道的确认之前,不会为转发的包写入确认。这种转发方法引入了一种情况:合法通道可能变得依赖于恶意通道。如果恶意通道不确认包,合法通道似乎无法转换到FLUSHCOMPLETE
状态,因此无法完成升级(无论是当前还是未来的升级)。
POC首先创建合法的transfer v2通道:在ibc-0上的channel-0 <-> 在ibc-1上的channel-0。这部分由中继器处理。
然后在应用程序中打开另外两个通道:在ibc-0上的channel-1 <- 我的本地链ibc-1 || 我的本地链ibc-0 -> 在ibc-1上的channel-1。
并提交如下接收包:
ibc0Cw.ReceivePacketV2(ibc0Path, types.FungibleTokenPacketDataV2{
Tokens: []types.Token{
{
Amount: "1",
Denom: types.Denom{Base: "x"},
},
},
Memo: "",
Sender: ibc1Cw.Address(),
Receiver: ibc0Cw.Address(),
Forwarding: types.ForwardingPacketData{Hops: []types.Hop{
{ChannelId: legitChannel, PortId: port}, {ChannelId: ibc0Path.Dest.ChanId, PortId: port},
}, DestinationMemo: ""},
})
包在ibc-0上的channel-1被接收,由于转发功能,它通过channel-0提交到ibc-1上的channel-0,然后再次转发到我的ibc-1上的channel-1。因此,它不会写入任何确认,但会写入收据。这意味着我们将无法使其超时。
最终结果是:ibc-0上的合法channel-0将持续拥有已提交(传输中)的包,直到我的ibc-1上的channel-1收到确认为止(而这不会发生)。受害者通道是ibc-0上的channel-0,最终将持有这个承诺。
POC本身并不是一个新场景,它只是展示了可以在合法通道上创建这些永久承诺的场景。
POC中使用的方法在https://github.com/h1uf/ibc-tools/blob/master/chain/wrapper.go中定义。
复现环境要求:
应用程序通过我的代理链建立channel-1到channel-1的连接。在https://gist.github.com/unknownfeature/b5c7323df65300a4699a3fa5471e9153#file-gistfile1-txt-L79之前的代码只是建立通道,然后在ibc-0上的channel-1接收包。
通过转发功能,Transfer v2通道可能变得无法升级,恶意通道可以在合法通道上创建将保持未确认状态的承诺。这似乎不是预期行为,因为承诺不应该永远保持已提交状态(除非除了提到的两个地方之外还有其他删除位置)。通过转发功能的这一部分引入的对潜在恶意通道的依赖似乎没有得到充分认识。
从目前来看,似乎没有其他方法可以删除承诺,而且通道似乎无法在不刷新所有包的情况下变为FLUSHCOMPLETE
状态(尽管可能遗漏了某些细节)。
最终,Transfer v2功能被撤回,通道可升级性被移除,从而解决了这个问题。该问题被归类为低严重性问题,并获得了2,000美元的赏金。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。