攒成长值也能赢iPhone 16?动态第 1️⃣ 1️⃣ 期夏季成长值抽奖大狂欢火热开启!
总奖池超 $10,000+,iPhone 16 ProMax 512G、Gate限量周边、热门代币等你来抽!
立即抽奖 👉 https://www.gate.com/activities/pointprize?now_period=11
如何快速赚成长值?
1️⃣ 进入【动态】,点击头像旁标识进入【社区中心】
2️⃣ 完成发帖、评论、点赞、发言等日常任务,成长值拿不停
🎁 本期新增“碎片兑换”玩法,集齐碎片即可兑换限量Gate周边!
100%有奖,抽到赚到,大奖等你抱走,赶紧试试手气!
截止于 6月4日 24:00 (UTC+8)
详情: https://www.gate.com/announcements/article/45185
#社区成长值#
Stride 空投漏洞解析
撰文:Will
**前言:**本文描述了 Jump Crypto 在 Stride 空投程序中发现的一个漏洞:Stride 是一个 Cosmos 链,用于在 Cosmos 生态系统中进行流动性质押。这个问题可能会让攻击者窃取 Stride 上所有无人认领的空投。在发现时,超过 160 万 STRD(相当于大约 400 万美元)处于危险之中。Jump 私下向 Stride 贡献者报告了该漏洞,该问题现已修复,通过努力没有发生恶意利用的事件。原文链接地址[1]
Stride 上的空投
Stride 定期对其原生 [$STRD]代币进行大量空投,以激励网络活动并在广泛的各方群体中分散治理。分配和领取空投的代码在 x/claim[2]模块中实现。空投分配是通过 LoadAllocationData[3]函数定义的,该函数加载一个包含地址和空投分配的分配文件。对于大多数空投,加载的地址描述了其他 Cosmos 链上的用户,例如 Osmosis 或 Juno,因此代码首先使用 utils.ConvertAddressToStrideAddress 函数将它们转换为 Stride 地址。
对于空投中的每个帐户,该模块都会创建一个 ClaimRecord[4],其中包含特定空投的空投标识符、转换后的地址以及分配给用户的代币数量。创建 ClaimRecord 后,具有相应 Stride 地址的用户可以通过向链发送 MsgClaimFreeAmount[5]来领取他们的空投。
但是,此实现在最近的 EVMOS 空投期间不起作用,因为 utils.ConvertAddressToStrideAddress 函数将 Evmos 地址映射到不可访问的 Stride 地址。这是因为 EVMOS 地址是使用硬币类型 60 派生的,而 Stride 地址是使用硬币类型 118 派生的。
为了让受影响的用户仍然可以领取空投,该团队添加了通过跨链 IBC 更新无人认领的 ClaimRecord 的目标地址的功能来自相应 EVMOS 帐户的消息。此更新机制作为 x/autopilot 模块的一部分实现。x/autopilot[6]拦截传入的 IBC ICS-20 传输并尝试从其备忘录或接收方字段中提取特定于 Stride 的指令(接收方字段在 v5 之前的 IBC 版本中兼作备忘录字段):
func(imIBCModule)OnRecvPacket(
ctxsdk.Context,
packetchanneltypes.Packet,
relayersdk.AccAddress,
)ibcexported.Acknowledgement{
//NOTE:acknowledgmentwillbewrittensynchronouslyduringIBChandlerution.
datatransfertypes.FungibleTokenPacketData
iferr:=transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
[..]
//ibc-gov5hasaMemofieldthatcanstoreforwardinginfo
//Forolderversionofibc-go,thedatamustbestoredinthereceiverfield
metadatastring
ifdata.Memo!=""{//ibc-gov5+
metadata=data.Memo
}else{//beforeibc-gov5
metadata=data.Receiver
}
[..]
//parseoutanyforwardinginfo
packetForwardMetadata,err:=types.ParsePacketMetadata(metadata)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
//Iftheparsedmetadataisnil,thatmeansthereisnoforwardinglogic
//Passthepacketdowntothenextmiddleware
ifpacketForwardMetadata==nil{
returnim.app.OnRecvPacket(ctx,packet,relayer)
}
//ModifythepacketdatabyreplacingtheJSONmetadatafieldwithareceiveraddress
//toallowthepackettocontinuedownthestack
newData:=data
newData.Receiver=packetForwardMetadata.Receiver
bz,err:=transfertypes.ModuleCdc.MarshalJSON(&newData)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
newPacket:=packet
newPacket.Data=bz
//Passthenewpacketdownthemiddlewarestackfirst
ack:=im.app.OnRecvPacket(ctx,newPacket,relayer)
if!ack.Success(){
returnack
}
autopilotParams:=im.keeper.GetParams(ctx)
//Ifthetransferwassuccessful,thenroutetothecorrespondingmodule,ifapplicable
switchroutingInfo:=packetForwardMetadata.RoutingInfo.(type){
casetypes.StakeibcPacketMetadata:
[...]
casetypes.ClaimPacketMetadata:
//Ifclaimroutingisinactive(butthepackethadroutinginfointhememo)returnanackerror
[..]
im.keeper.Logger(ctx).Info(fmt.Sprintf("Forwaringpacketfrom%stoclaim",newData.Sender))
iferr:=im.keeper.TryUpdateAirdropClaim(ctx,newData,routingInfo);err!=nil{
im.keeper.Logger(ctx).Error(fmt.Sprintf("Errorupdatingairdropclaimfromautopilotfor%s:%s",newData.Sender,err.Error()))
returnchanneltypes.NewErrorAcknowledgement(err)
}
returnack
default:
returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))
}
}
如果包含的元数据表明传入传输是空投声明,则模块调用 TryUpdateAirdropClaim 函数:
func(kKeeper)TryUpdateAirdropClaim(
ctxsdk.Context,
datatransfertypes.FungibleTokenPacketData,
packetMetadatatypes.ClaimPacketMetadata,
)error{
[..]
//grabrelevantaddresses
senderStrideAddress:=utils.ConvertAddressToStrideAddress(data.Sender)
ifsenderStrideAddress==""{
returnerrorsmod.Wrapf(sdkerrors.ErrInvalidAddress,fmt.Sprintf("invalidsenderaddress(%s)",data.Sender))
}
newStrideAddress:=packetMetadata.StrideAddress
//updatetheairdrop
airdropId:=packetMetadata.AirdropId
k.Logger(ctx).Info(fmt.Sprintf("updatingairdropaddress%s(orig%s)to%sforairdrop%s",
senderStrideAddress,data.Sender,newStrideAddress,airdropId))
returnk.claimKeeper.UpdateAirdropAddress(ctx,senderStrideAddress,newStrideAddress,airdropId)
}
函数转换发送方 IBC 数据包的地址到名为 senderStrideAddress 的 Stride 地址,并从数据包元数据中提取 airdropId 和新的空投地址 newStrideAddress。然后它调用 UpdateAirdropAddress 来更新一个打开的 ClaimRecord,该记录与 senderStrideAddress 和 airdropId 的组合匹配到新地址。
随着 ClaimRecord 的更新,newStrideAddress 现在可以领取空投了。需要注意的重要一点是,此更新机制仅受 IBC 数据包内指定的发件人地址的保护。Stride 不执行任何其他验证来确保空投的更新是由真正的接收者触发的。
要了解为什么这是一个严重的漏洞,我们需要仔细研究 IBC,即区块链间通信协议。
IBC 安全
IBC 是一种基于轻客户端的跨链通信机制。与经典网络协议类似,核心 IBC 模块抽象了许多底层细节,使开发人员可以轻松地在其上构建自己的集成。将一个支持 IBC 的链(链 A)连接到另一个支持 IBC 的链(链 B)看起来有点像这样:
CreatedsolomachineclientonIBCenabledchain[ClientID=06-solomachine-6]
Createdtendermintclientonsolomachine[ClientID=07-tendermint-M48f]
InitializedconnectiononIBCenabledchain[ConnectionID=connection-4]
Initializedconnectiononsolomachine[ConnectionID=connection-Kinb]
ConfirmedconnectiononIBCenabledchain[ConnectionID=connection-4]
Confirmedconnectiononsolomachine[ConnectionID=connection-Kinb]
InitializedchannelonIBCenabledchain[ChannelID=channel-0]
Initializedchannelonsolomachine[ChannelID=channel-wwl6]
ConfirmedchannelonIBCenabledchain[ChannelID=channel-0]
Confirmedchannelonsolomachine[ChannelID=channel-wwl6]
Connectionestablished!
第一步,在链 B 上创建链 A 的 IBC 轻客户端,反之亦然. IBC 客户端由其客户端 ID 唯一标识,用于跟踪和验证远程链的状态。创建客户端后,它们可以通过一个连接来连接,该连接是通过四次握手启动的。这在链 A 上创建了一个 ConnectionEnd,链 B 的轻客户端在 A 上,另一个在链 B 上,链 A 的轻客户端在 B 上。连接一旦创建就会持久,并受到两个轻客户端的加密保护。
通过连接进行的通信还分为不同的通道。通道由底层连接以及源端口和目标端口标识。每个端口标识通过 IBC 连接的相应链上的一个模块。与 Connection 关联的 ChannelEnd 在两个链上创建并通过 channel-id 标识。现在可以通过已建立的通道在两个链之间传输数据。
重要的是要记住,默认情况下 IBC 是一种无需许可的协议。这意味着任何人都可以连接任何两条支持 IBC 的链,而无需事先授权或批准。实际上,IBC 支持所谓的 Solo Machines[7] 标准,客户端不代表区块链,而是代表单个主机或机器。由于 IBC 数据包内容完全由发送方(通常是源链上的源模块)控制,因此根据传入的 IBC 数据包执行特权操作的模块始终需要验证消息是否来自可信通道。
漏洞
然而,就 Stride 而言 x/autopilot 模块中缺少通道检查。该代码假定具有特定发件人地址的 ICS-20 IBC 数据包只能由对该地址有控制权的人发送。如果我们只考虑 EVMOS 等可信赖合作伙伴链上的传输模块,这是正确的,但攻击者可以简单地发送完全受控的 IBC 数据包数据,以使用他们控制下的恶意 IBC 客户端。利用此漏洞相对简单:
漏洞修复
在收到我们的及时报告后,Stride 贡献者迅速从 Airdrop 经销商钱包中取出所有资金,以确保没有资金处于风险之中。实施的长期修复确保 IBC 空投地址更新数据包通过正确的可信 IBC 通道到达。
结论
通过 IBC 对跨链通信的强大支持是 Cosmos 生态系统的独特优势。虽然 IBC 建立在可靠的加密原语之上,但与其安全集成需要对底层信任模型有很好的理解。在 IBC 之上构建的开发人员和审查 IBC 集成的安全工程师应该仔细审查暴露给恶意 IBC 客户端或渠道的攻击面。我们要感谢 Stride 贡献者对这个问题的专业处理和快速响应。