Pengembang yang membangun di atas IBC dan insinyur keamanan yang meninjau integrasi IBC harus dengan hati-hati meninjau permukaan serangan yang diekspos ke klien atau saluran IBC yang berbahaya.
Ditulis oleh: Akan
Kata Pengantar: Artikel ini menjelaskan kerentanan yang ditemukan Jump Crypto dalam program airdrop Stride: Stride adalah rantai Kosmos yang digunakan untuk mengintai cairan di ekosistem Kosmos. Masalah ini dapat memungkinkan penyerang mencuri semua airdrop yang tidak diklaim di Stride. Pada saat ditemukan, lebih dari 1,6 juta STRD (setara dengan sekitar $4 juta) berada dalam risiko. Jump secara pribadi melaporkan kerentanan kepada kontributor Stride, dan masalah tersebut telah diperbaiki, tanpa eksploitasi jahat yang terjadi sebagai hasil dari upaya kami. Alamat tautan asli [1]
Airdrop di Stride
Stride secara teratur melakukan airdrop besar dari token [$STRD] aslinya untuk memberi insentif pada aktivitas jaringan dan mendesentralisasi tata kelola di berbagai kelompok pihak. Kode untuk mengalokasikan dan mengklaim airdrop ada di x/claim [2] diimplementasikan dalam modul. Alokasi airdrop adalah melalui LoadAllocationData [3] ditentukan oleh fungsi yang memuat file alokasi yang berisi alamat dan alokasi airdrop. Untuk sebagian besar airdrop, alamat yang dimuat menggambarkan pengguna di rantai Cosmos lain, seperti Osmosis atau Juno, jadi kode pertama-tama mengonversinya menjadi alamat Stride menggunakan fungsi utils.ConvertAddressToStrideAddress.
Untuk setiap akun di airdrop, modul membuat ClaimRecord [4] , yang berisi pengidentifikasi airdrop untuk airdrop tertentu, alamat yang dikonversi, dan jumlah token yang dialokasikan untuk pengguna. Setelah membuat ClaimRecord, pengguna dengan alamat Stride yang sesuai dapat mengirimkan MsgClaimFreeAmount ke rantai [5] Datang dan klaim airdrop mereka.
Namun, implementasi ini tidak berfungsi selama airdrop EVMOS baru-baru ini karena fungsi utils.ConvertAddressToStrideAddress memetakan alamat Evmos ke alamat Stride yang tidak dapat diakses. Ini karena alamat EVMOS diturunkan menggunakan koin tipe 60, sedangkan alamat Stride diturunkan menggunakan koin tipe 118.
Agar pengguna yang terpengaruh masih dapat mengklaim airdrop, tim menambahkan kemampuan untuk memperbarui alamat target ClaimRecord yang tidak diklaim melalui pesan IBC lintas rantai dari akun EVMOS yang sesuai. Mekanisme pembaruan ini diimplementasikan sebagai bagian dari modul x/autopilot. x/pilot otomatis [6] Mencegat transmisi IBC ICS-20 yang masuk dan mencoba mengekstrak instruksi khusus Stride dari bidang memo atau penerima (bidang penerima berfungsi ganda sebagai bidang memo dalam versi IBC sebelum v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Konteks,
packetchanneltypes.Paket,
relayersdk.AccAddress,
)ibcexported.Ucapan Terima Kasih{
//CATATAN:pengakuan akan ditulis secara serempak selama penanganan IBC.
Fungsi mengubah alamat paket IBC pengirim menjadi alamat Stride bernama senderStrideAddress, dan mengekstrak airdropId dan alamat airdrop baru newStrideAddress dari metadata paket. Ini kemudian memanggil UpdateAirdropAddress untuk memperbarui ClaimRecord terbuka yang cocok dengan kombinasi senderStrideAddress dan airdropId ke alamat baru.
Dengan pembaruan ClaimRecord, newStrideAddress sekarang dapat mengklaim airdrop. Penting untuk diperhatikan bahwa mekanisme pembaruan ini hanya dilindungi oleh alamat pengirim yang ditentukan dalam paket IBC. Stride tidak melakukan verifikasi lain untuk memastikan bahwa pembaruan airdrop dipicu oleh penerima sebenarnya.
Untuk memahami mengapa ini merupakan kerentanan yang serius, kita perlu melihat lebih dekat pada IBC, protokol komunikasi antar-blockchain.
Keamanan IBC
IBC adalah mekanisme komunikasi lintas rantai berbasis klien yang ringan. Mirip dengan protokol jaringan klasik, modul inti IBC mengabstraksikan banyak detail tingkat rendah, memungkinkan developer untuk dengan mudah membangun integrasi mereka sendiri di atasnya. Menghubungkan satu rantai yang mendukung IBC (Rantai A) ke rantai lain yang mendukung IBC (Rantai B) terlihat seperti ini:
Pada langkah pertama, klien ringan IBC dari rantai A dibuat pada rantai B, dan sebaliknya.Klien IBC diidentifikasi secara unik oleh ID kliennya, yang digunakan untuk melacak dan memverifikasi status rantai jarak jauh. Setelah klien dibuat, mereka dapat terhubung melalui koneksi, yang dimulai dengan jabat tangan empat arah. Ini menciptakan ConnectionEnd pada rantai A dengan klien ringan rantai B di A, dan satu lagi di rantai B dengan klien ringan rantai A di B. Koneksi, setelah dibuat, tetap ada dan dienkripsi oleh dua klien ringan.
Komunikasi melalui koneksi juga dibagi menjadi saluran yang berbeda. Saluran diidentifikasi oleh koneksi yang mendasari dan port sumber dan tujuan. Setiap port mengidentifikasi modul pada rantai yang sesuai yang terhubung melalui IBC. ChannelEnd yang terkait dengan Connection dibuat di kedua rantai dan diidentifikasi oleh channel-id. Data sekarang dapat ditransfer antara dua rantai melalui saluran yang dibuat.
Penting untuk diingat bahwa IBC adalah protokol tanpa izin secara default. Ini berarti siapa pun dapat menghubungkan dua rantai yang mendukung IBC tanpa otorisasi atau persetujuan sebelumnya. Nyatanya, IBC mendukung apa yang disebut Mesin Solo [7] Standar, klien tidak mewakili blockchain, tetapi satu host atau mesin. Karena konten paket IBC sepenuhnya dikontrol oleh pengirim (biasanya modul sumber pada rantai sumber), modul yang melakukan operasi istimewa pada paket IBC yang masuk selalu perlu memverifikasi bahwa pesan tersebut berasal dari saluran tepercaya.
Kerentanan
Namun, sejauh menyangkut Stride, pemeriksaan saluran tidak ada dalam modul x/autopilot. Kode mengasumsikan bahwa paket IBC ICS-20 dengan alamat pengirim tertentu hanya dapat dikirim oleh seseorang yang memiliki kendali atas alamat tersebut. Ini benar jika kami hanya mempertimbangkan modul transportasi pada rantai mitra tepercaya seperti EVMOS, tetapi penyerang dapat dengan mudah mengirim data paket IBC yang dikontrol sepenuhnya untuk menggunakan klien IBC jahat di bawah kendali mereka. Eksploitasi kerentanan ini relatif sederhana:
Buat klien IBC yang berbahaya
Menggunakan klien jahat Craft untuk membuat saluran IBC ke modul transportasi Stride IBC,
Dan kirim transfer IBC jahat menggunakan alamat ClaimRecords yang tidak diklaim sebagai bidang Dari. Gunakan kolom memo ClaimMetadata untuk memicu autopilot dan memperbarui alamat airdrop ke akun Stride yang dikontrol penyerang.
Curi airdrop dengan mengirimkan MsgClaimFreeAmount ke modul x/claim
Perbaikan kerusakan
Setelah menerima laporan cepat kami, kontributor Stride dengan cepat menarik semua dana dari dompet pengecer Airdrop untuk memastikan tidak ada dana yang berisiko. Menerapkan perbaikan jangka panjang untuk memastikan bahwa paket pembaruan alamat airdrop IBC tiba melalui saluran IBC tepercaya yang benar.
Kesimpulannya
Dukungan kuat untuk komunikasi lintas rantai melalui IBC adalah keuntungan unik dari ekosistem Cosmos. Sementara IBC dibangun di atas primitif kriptografi yang solid, mengintegrasikannya secara aman membutuhkan pemahaman yang baik tentang model kepercayaan yang mendasarinya. Pengembang yang membangun di atas IBC dan insinyur keamanan yang meninjau integrasi IBC harus dengan hati-hati meninjau permukaan serangan yang diekspos ke klien atau saluran IBC yang berbahaya. Kami ingin berterima kasih kepada kontributor Stride atas penanganan profesional dan tanggapan cepat mereka terhadap masalah ini.
Tautan Eksternal WeChat
[1] Alamat tautan asli:
[2] x/klaim:
[3] LoadAllocationData:
[4] Rekam Klaim:
[5] MsgClaimFreeAmount:
[6] x/pilot otomatis:
[7] Mesin Tunggal:
Lihat Asli
Konten ini hanya untuk referensi, bukan ajakan atau tawaran. Tidak ada nasihat investasi, pajak, atau hukum yang diberikan. Lihat Penafian untuk pengungkapan risiko lebih lanjut.
Analisis Kerentanan Stride Airdrop
Ditulis oleh: Akan
Kata Pengantar: Artikel ini menjelaskan kerentanan yang ditemukan Jump Crypto dalam program airdrop Stride: Stride adalah rantai Kosmos yang digunakan untuk mengintai cairan di ekosistem Kosmos. Masalah ini dapat memungkinkan penyerang mencuri semua airdrop yang tidak diklaim di Stride. Pada saat ditemukan, lebih dari 1,6 juta STRD (setara dengan sekitar $4 juta) berada dalam risiko. Jump secara pribadi melaporkan kerentanan kepada kontributor Stride, dan masalah tersebut telah diperbaiki, tanpa eksploitasi jahat yang terjadi sebagai hasil dari upaya kami. Alamat tautan asli [1]
Airdrop di Stride
Stride secara teratur melakukan airdrop besar dari token [$STRD] aslinya untuk memberi insentif pada aktivitas jaringan dan mendesentralisasi tata kelola di berbagai kelompok pihak. Kode untuk mengalokasikan dan mengklaim airdrop ada di x/claim [2] diimplementasikan dalam modul. Alokasi airdrop adalah melalui LoadAllocationData [3] ditentukan oleh fungsi yang memuat file alokasi yang berisi alamat dan alokasi airdrop. Untuk sebagian besar airdrop, alamat yang dimuat menggambarkan pengguna di rantai Cosmos lain, seperti Osmosis atau Juno, jadi kode pertama-tama mengonversinya menjadi alamat Stride menggunakan fungsi utils.ConvertAddressToStrideAddress.
Untuk setiap akun di airdrop, modul membuat ClaimRecord [4] , yang berisi pengidentifikasi airdrop untuk airdrop tertentu, alamat yang dikonversi, dan jumlah token yang dialokasikan untuk pengguna. Setelah membuat ClaimRecord, pengguna dengan alamat Stride yang sesuai dapat mengirimkan MsgClaimFreeAmount ke rantai [5] Datang dan klaim airdrop mereka.
Namun, implementasi ini tidak berfungsi selama airdrop EVMOS baru-baru ini karena fungsi utils.ConvertAddressToStrideAddress memetakan alamat Evmos ke alamat Stride yang tidak dapat diakses. Ini karena alamat EVMOS diturunkan menggunakan koin tipe 60, sedangkan alamat Stride diturunkan menggunakan koin tipe 118.
Agar pengguna yang terpengaruh masih dapat mengklaim airdrop, tim menambahkan kemampuan untuk memperbarui alamat target ClaimRecord yang tidak diklaim melalui pesan IBC lintas rantai dari akun EVMOS yang sesuai. Mekanisme pembaruan ini diimplementasikan sebagai bagian dari modul x/autopilot. x/pilot otomatis [6] Mencegat transmisi IBC ICS-20 yang masuk dan mencoba mengekstrak instruksi khusus Stride dari bidang memo atau penerima (bidang penerima berfungsi ganda sebagai bidang memo dalam versi IBC sebelum v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Konteks,
packetchanneltypes.Paket,
relayersdk.AccAddress,
)ibcexported.Ucapan Terima Kasih{
//CATATAN:pengakuan akan ditulis secara serempak selama penanganan IBC.
datatransfertypes.FungibleTokenPacketData
iferr:=transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
[..]
//ibc-gov5hasaMemofieldthatcanstoreforwardinginfo
//Untuk versi lama dari ibc-go, data harus disimpan di kolom penerima
string metadata
ifdata.Memo!=""{//ibc-gov5+
metadata=data.Memo
}else{//beforeibc-gov5
metadata=data.Penerima
}
[..]
//parseoutanyforwardinginfo
packetForwardMetadata,err:=types.ParsePacketMetadata(metadata)
iferr!=nihil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
// Jika metadata yang diurai nihil, itu berarti tidak ada logika penerusan
// Lewati paket ke middleware berikutnya
ifpacketForwardMetadata==nil{
returnim.app.OnRecvPacket(ctx,packet,relayer)
}
// Modifikasi data paket dengan mengganti bidang metadata JSON dengan alamat penerima
// untuk mengizinkan paket untuk melanjutkan ke tumpukan
DataBaru:=data
newData.Receiver=packetForwardMetadata.Receiver
bz,err:=transfertypes.ModuleCdc.MarshalJSON(&newData)
iferr!=nihil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
newPacket := paket
newPacket.Data=bz
//Passthenewpacketdownthemiddlewarestackpertama
ack:=im.app.OnRecvPacket(ctx,newPacket,relayer)
jika!ack.Sukses(){
returnack
}
autopilotParams:=im.keeper.GetParams(ctx)
// Jika transfer berhasil, lalu rutekan ke modul yang sesuai, jika berlaku
switchroutingInfo:=packetForwardMetadata.RoutingInfo.(tipe){
casetypes.StakeibcPacketMetadata:
[...]
casetypes.ClaimPacketMetadata:
//Jikaclaimrouting tidak aktif(tetapipaket memiliki info perutean dimemo)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
bawaan:
returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))
}
}
Jika metadata yang disertakan menunjukkan bahwa transfer masuk adalah klaim airdrop, modul akan memanggil fungsi TryUpdateAirdropClaim :
func(kKeeper)CobaUpdateAirdropClaim(
ctxsdk.Konteks,
datatransfertypes.FungibleTokenPacketData,
packetMetadatatypes.ClaimPacketMetadata,
)kesalahan{
[..]
//grabrelevantaddresses
senderStrideAddress:=utils.ConvertAddressToStrideAddress(data.Sender)
ifsenderStrideAddress==""{
returnerrorsmod.Wrapf(sdkerrors.ErrInvalidAddress,fmt.Sprintf("invalidsenderaddress(%s)",data.Sender))
}
newStrideAddress:=packetMetadata.StrideAddress
// perbarui airdrop
airdropId:=packetMetadata.AirdropId
k.Logger(ctx).Info(fmt.Sprintf("memperbarui alamatairdrop%s(asli%s)ke%sforairdrop%s",
senderStrideAddress,data.Sender,newStrideAddress,airdropId))
returnk.claimKeeper.UpdateAirdropAddress(ctx,senderStrideAddress,newStrideAddress,airdropId)
}
Fungsi mengubah alamat paket IBC pengirim menjadi alamat Stride bernama senderStrideAddress, dan mengekstrak airdropId dan alamat airdrop baru newStrideAddress dari metadata paket. Ini kemudian memanggil UpdateAirdropAddress untuk memperbarui ClaimRecord terbuka yang cocok dengan kombinasi senderStrideAddress dan airdropId ke alamat baru.
Dengan pembaruan ClaimRecord, newStrideAddress sekarang dapat mengklaim airdrop. Penting untuk diperhatikan bahwa mekanisme pembaruan ini hanya dilindungi oleh alamat pengirim yang ditentukan dalam paket IBC. Stride tidak melakukan verifikasi lain untuk memastikan bahwa pembaruan airdrop dipicu oleh penerima sebenarnya.
Untuk memahami mengapa ini merupakan kerentanan yang serius, kita perlu melihat lebih dekat pada IBC, protokol komunikasi antar-blockchain.
Keamanan IBC
IBC adalah mekanisme komunikasi lintas rantai berbasis klien yang ringan. Mirip dengan protokol jaringan klasik, modul inti IBC mengabstraksikan banyak detail tingkat rendah, memungkinkan developer untuk dengan mudah membangun integrasi mereka sendiri di atasnya. Menghubungkan satu rantai yang mendukung IBC (Rantai A) ke rantai lain yang mendukung IBC (Rantai B) terlihat seperti ini:
CreatedsolomachineclientonIBCenabledchain[ClientID=06-solomachine-6]
Createdtendermintclientonsolomachine[ClientID=07-tendermint-M48f]
Koneksi yang diinisialisasi padaIBCenabledchain[ConnectionID=koneksi-4]
Mesinkoneksionsolo yang diinisialisasi[ConnectionID=connection-Kinb]
ConfirmedconnectiononIBCenabledchain[ConnectionID=koneksi-4]
Confirmedconnectiononsolomachine[ConnectionID=connection-Kinb]
DiinisialisasichannelonIBCenabledchain[ChannelID=channel-0]
Diinisialisasichannelonsolomachine[ChannelID=channel-wwl6]
DikonfirmasichannelonIBCenabledchain[ChannelID=channel-0]
Dikonfirmasichannelonsolomachine[ChannelID=channel-wwl6]
Koneksi terjalin!
Pada langkah pertama, klien ringan IBC dari rantai A dibuat pada rantai B, dan sebaliknya.Klien IBC diidentifikasi secara unik oleh ID kliennya, yang digunakan untuk melacak dan memverifikasi status rantai jarak jauh. Setelah klien dibuat, mereka dapat terhubung melalui koneksi, yang dimulai dengan jabat tangan empat arah. Ini menciptakan ConnectionEnd pada rantai A dengan klien ringan rantai B di A, dan satu lagi di rantai B dengan klien ringan rantai A di B. Koneksi, setelah dibuat, tetap ada dan dienkripsi oleh dua klien ringan.
Komunikasi melalui koneksi juga dibagi menjadi saluran yang berbeda. Saluran diidentifikasi oleh koneksi yang mendasari dan port sumber dan tujuan. Setiap port mengidentifikasi modul pada rantai yang sesuai yang terhubung melalui IBC. ChannelEnd yang terkait dengan Connection dibuat di kedua rantai dan diidentifikasi oleh channel-id. Data sekarang dapat ditransfer antara dua rantai melalui saluran yang dibuat.
Penting untuk diingat bahwa IBC adalah protokol tanpa izin secara default. Ini berarti siapa pun dapat menghubungkan dua rantai yang mendukung IBC tanpa otorisasi atau persetujuan sebelumnya. Nyatanya, IBC mendukung apa yang disebut Mesin Solo [7] Standar, klien tidak mewakili blockchain, tetapi satu host atau mesin. Karena konten paket IBC sepenuhnya dikontrol oleh pengirim (biasanya modul sumber pada rantai sumber), modul yang melakukan operasi istimewa pada paket IBC yang masuk selalu perlu memverifikasi bahwa pesan tersebut berasal dari saluran tepercaya.
Kerentanan
Namun, sejauh menyangkut Stride, pemeriksaan saluran tidak ada dalam modul x/autopilot. Kode mengasumsikan bahwa paket IBC ICS-20 dengan alamat pengirim tertentu hanya dapat dikirim oleh seseorang yang memiliki kendali atas alamat tersebut. Ini benar jika kami hanya mempertimbangkan modul transportasi pada rantai mitra tepercaya seperti EVMOS, tetapi penyerang dapat dengan mudah mengirim data paket IBC yang dikontrol sepenuhnya untuk menggunakan klien IBC jahat di bawah kendali mereka. Eksploitasi kerentanan ini relatif sederhana:
Perbaikan kerusakan
Setelah menerima laporan cepat kami, kontributor Stride dengan cepat menarik semua dana dari dompet pengecer Airdrop untuk memastikan tidak ada dana yang berisiko. Menerapkan perbaikan jangka panjang untuk memastikan bahwa paket pembaruan alamat airdrop IBC tiba melalui saluran IBC tepercaya yang benar.
Kesimpulannya
Dukungan kuat untuk komunikasi lintas rantai melalui IBC adalah keuntungan unik dari ekosistem Cosmos. Sementara IBC dibangun di atas primitif kriptografi yang solid, mengintegrasikannya secara aman membutuhkan pemahaman yang baik tentang model kepercayaan yang mendasarinya. Pengembang yang membangun di atas IBC dan insinyur keamanan yang meninjau integrasi IBC harus dengan hati-hati meninjau permukaan serangan yang diekspos ke klien atau saluran IBC yang berbahaya. Kami ingin berterima kasih kepada kontributor Stride atas penanganan profesional dan tanggapan cepat mereka terhadap masalah ini.