Розробники, які створюють поверхню IBC, та інженери з безпеки, які перевіряють інтеграцію IBC, повинні уважно перевірити поверхню атаки, яка піддається зловмисним клієнтам або каналам IBC.
Автор: Will
Передмова: У цій статті описано вразливість, яку Jump Crypto виявив у програмі розсилки Stride: Stride — це ланцюжок Cosmos, який використовується для ліквідних ставок в екосистемі Cosmos. Ця проблема може дозволити зловмиснику викрасти всі незатребувані аірдропи на Stride. На момент відкриття понад 1,6 мільйона STRD (еквівалентно приблизно 4 мільйонам доларів США) перебували під загрозою. Jump приватно повідомив про вразливість учасників Stride, і з тих пір проблему було вирішено, і в результаті наших зусиль не було зловмисників. Оригінальна адреса посилання [1]
Airdrop на Stride
Stride регулярно проводить великі роздачі свого рідного токена [$STRD], щоб стимулювати мережеву активність і децентралізувати управління серед широкої групи сторін. Код для розподілу та вимоги airdrops знаходиться на x/claim [2] реалізовано в модулі. Розподіл Airdrop відбувається через LoadAllocationData [3] визначається функцією, яка завантажує файл розподілу, що містить адреси та виділення airdrop. Для більшості airdrops завантажені адреси описують користувачів інших мереж Cosmos, таких як Osmosis або Juno, тому код спочатку перетворює їх на адреси Stride за допомогою функції utils.ConvertAddressToStrideAddress.
Для кожного облікового запису в airdrop модуль створює ClaimRecord [4] , який містить ідентифікатор airdrop для конкретного airdrop, перетворену адресу та кількість токенів, виділених користувачеві. Після створення ClaimRecord користувач із відповідною адресою Stride може надіслати MsgClaimFreeAmount до ланцюжка [5] Приходьте та вимагайте їх роздачі.
Однак ця реалізація не спрацювала під час останнього запуску EVMOS, оскільки функція utils.ConvertAddressToStrideAddress відображає адреси Evmos на адреси Stride, які недоступні. Це пояснюється тим, що адреси EVMOS виводяться з використанням типу монет 60, тоді як адреси Stride виводяться з використанням типу монет 118.
Для того, щоб постраждалі користувачі все ще могли вимагати airdrop, команда додала можливість оновлювати цільову адресу незатребуваного ClaimRecord через міжланцюгове повідомлення IBC з відповідного облікового запису EVMOS. Цей механізм оновлення реалізовано як частина модуля x/autopilot. x/автопілот [6] Перехопити вхідну передачу IBC ICS-20 і спробувати витягти специфічні для Stride інструкції з поля memo або одержувача (поле одержувача подвоюється як поле memo у версіях IBC до v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Контекст,
packetchanneltypes.Packet,
relayersdk.AccAddress,
)ibcexported.Acknowledgement{
//ПРИМІТКА: підтвердження буде написано синхронно під час IBChandlerution.
Ця функція перетворює адресу відправника пакета IBC на адресу Stride під назвою senderStrideAddress і витягує airdropId і нову адресу airdrop newStrideAddress із метаданих пакета. Потім він викликає UpdateAirdropAddress, щоб оновити відкритий ClaimRecord, який відповідає комбінації senderStrideAddress і airdropId до нової адреси.
Завдяки оновленню ClaimRecord newStrideAddress тепер може вимагати еірдроп. Важливо зазначити, що цей механізм оновлення захищений лише адресою відправника, зазначеною в пакеті IBC. Stride не виконує жодних інших перевірок, щоб переконатися, що оновлення airdrops ініціювали реальні одержувачі.
Щоб зрозуміти, чому це серйозна вразливість, нам потрібно ближче розглянути IBC, протокол зв’язку між блоками.
IBC Security
IBC — це легкий міжланцюговий механізм зв’язку на основі клієнта. Подібно до класичних мережевих протоколів, основний модуль IBC абстрагує багато низькорівневих деталей, що дозволяє розробникам легко створювати власні інтеграції на його основі. Підключення одного ланцюга з підтримкою IBC (ланцюга A) до іншого ланцюга з підтримкою IBC (ланцюга B) виглядає приблизно так:
На першому кроці легкий клієнт IBC ланцюга A створюється в ланцюзі B. Клієнт IBC унікально ідентифікується за його ідентифікатором клієнта, який використовується для відстеження та перевірки стану віддаленого ланцюга. Після того, як клієнти створені, вони можуть підключатися через з’єднання, яке ініціюється чотиристороннім рукостисканням. Це створює ConnectionEnd у ланцюжку A з легкими клієнтами ланцюга B на A, а інший у ланцюжку B з легкими клієнтами ланцюга A на B. З’єднання після створення є постійними та шифруються двома легкими клієнтами.
Спілкування через підключення також поділяється на різні канали. Канали ідентифікуються основним підключенням і портами джерела та призначення. Кожен порт ідентифікує модуль у відповідному ланцюжку, підключеному через IBC. ChannelEnd, пов’язаний зі з’єднанням, створюється в обох ланцюгах і ідентифікується ідентифікатором каналу. Тепер дані можна передавати між двома ланцюгами через встановлений канал.
Важливо пам’ятати, що IBC за замовчуванням є протоколом без дозволу. Це означає, що будь-хто може з’єднати будь-які два ланцюги з підтримкою IBC без попереднього дозволу чи схвалення. Насправді IBC підтримує так звані Solo Machines [7] Стандартно клієнт представляє не блокчейн, а окремий хост або машину. Оскільки вміст пакету IBC повністю контролюється відправником (зазвичай модулем джерела в ланцюжку джерел), модулям, які виконують привілейовані операції над вхідними пакетами IBC, завжди потрібно перевіряти, чи повідомлення надійшло з надійного каналу.
Уразливості
Однак, що стосується Stride, перевірка каналів відсутня в модулі x/autopilot. Код припускає, що пакети ICS-20 IBC з конкретною адресою відправника можуть надсилатися лише тими, хто контролює цю адресу. Це вірно, якщо ми розглядаємо лише транспортні модулі в надійних партнерських ланцюгах, як-от EVMOS, але зловмисники можуть просто надсилати повністю контрольовані пакетні дані IBC, щоб використовувати шкідливі клієнти IBC під своїм контролем. Використання цієї вразливості відносно просте:
Створіть шкідливий клієнт IBC
Використовуючи шкідливий клієнт Craft для створення каналу IBC до транспортного модуля Stride IBC,
Надішліть зловмисний переказ IBC, використовуючи адресу незатребуваних ClaimRecords як поле From. Використовуйте поле пам’ятки ClaimMetadata, щоб запустити автопілот і оновити адресу airdrop для контрольованого зловмисником облікового запису Stride.
Викрадіть скидання, надіславши MsgClaimFreeAmount до модуля x/claim
Виправлення помилок
Отримавши наш швидкий звіт, учасник Stride швидко зняв усі кошти з гаманця торговельного посередника Airdrop, щоб переконатися, що кошти не знаходяться під загрозою. Реалізовано довгострокове виправлення, щоб гарантувати, що пакети оновлення адреси IBC airdrop надходять через правильний надійний канал IBC.
на завершення
Сильна підтримка міжланцюгового зв’язку через IBC є унікальною перевагою екосистеми Cosmos. Хоча IBC побудовано на надійних криптографічних примітивах, безпечна інтеграція з ним вимагає хорошого розуміння базової моделі довіри. Розробники, які створюють поверхню IBC, та інженери з безпеки, які перевіряють інтеграцію IBC, повинні уважно перевірити поверхню атаки, яка піддається зловмисним клієнтам або каналам IBC. Ми хотіли б подякувати учасникам Stride за професійне поводження та швидку відповідь на цю проблему.
Зовнішнє посилання WeChat
[1] Оригінальна адреса посилання:
[2] х/претензія:
[3] LoadAllocationData:
[4] ClaimRecord:
[5] MsgClaimFreeAmount:
[6] x/автопілот:
[7] Соло машини:
Переглянути оригінал
Контент має виключно довідковий характер і не є запрошенням до участі або пропозицією. Інвестиційні, податкові чи юридичні консультації не надаються. Перегляньте Відмову від відповідальності , щоб дізнатися більше про ризики.
Аналіз вразливості Stride Airdrop
Автор: Will
Передмова: У цій статті описано вразливість, яку Jump Crypto виявив у програмі розсилки Stride: Stride — це ланцюжок Cosmos, який використовується для ліквідних ставок в екосистемі Cosmos. Ця проблема може дозволити зловмиснику викрасти всі незатребувані аірдропи на Stride. На момент відкриття понад 1,6 мільйона STRD (еквівалентно приблизно 4 мільйонам доларів США) перебували під загрозою. Jump приватно повідомив про вразливість учасників Stride, і з тих пір проблему було вирішено, і в результаті наших зусиль не було зловмисників. Оригінальна адреса посилання [1]
Airdrop на Stride
Stride регулярно проводить великі роздачі свого рідного токена [$STRD], щоб стимулювати мережеву активність і децентралізувати управління серед широкої групи сторін. Код для розподілу та вимоги airdrops знаходиться на x/claim [2] реалізовано в модулі. Розподіл Airdrop відбувається через LoadAllocationData [3] визначається функцією, яка завантажує файл розподілу, що містить адреси та виділення airdrop. Для більшості airdrops завантажені адреси описують користувачів інших мереж Cosmos, таких як Osmosis або Juno, тому код спочатку перетворює їх на адреси Stride за допомогою функції utils.ConvertAddressToStrideAddress.
Для кожного облікового запису в airdrop модуль створює ClaimRecord [4] , який містить ідентифікатор airdrop для конкретного airdrop, перетворену адресу та кількість токенів, виділених користувачеві. Після створення ClaimRecord користувач із відповідною адресою Stride може надіслати MsgClaimFreeAmount до ланцюжка [5] Приходьте та вимагайте їх роздачі.
Однак ця реалізація не спрацювала під час останнього запуску EVMOS, оскільки функція utils.ConvertAddressToStrideAddress відображає адреси Evmos на адреси Stride, які недоступні. Це пояснюється тим, що адреси EVMOS виводяться з використанням типу монет 60, тоді як адреси Stride виводяться з використанням типу монет 118.
Для того, щоб постраждалі користувачі все ще могли вимагати airdrop, команда додала можливість оновлювати цільову адресу незатребуваного ClaimRecord через міжланцюгове повідомлення IBC з відповідного облікового запису EVMOS. Цей механізм оновлення реалізовано як частина модуля x/autopilot. x/автопілот [6] Перехопити вхідну передачу IBC ICS-20 і спробувати витягти специфічні для Stride інструкції з поля memo або одержувача (поле одержувача подвоюється як поле memo у версіях IBC до v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Контекст,
packetchanneltypes.Packet,
relayersdk.AccAddress,
)ibcexported.Acknowledgement{
//ПРИМІТКА: підтвердження буде написано синхронно під час IBChandlerution.
datatransfertypes.FungibleTokenPacketData
iferr:=transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
[..]
//ibc-gov5hasaMemofield, яке може зберігати інформацію про пересилання
//Для старішої версії fibc-go дані потрібно зберігати в полі отримувача
рядок метаданих
ifdata.Memo!=""{//ibc-gov5+
metadata=data.Memo
}else{//foreibc-gov5
metadata=data.Receiver
}
[..]
//розібрати будь-яку інформацію про пересилання
packetForwardMetadata,err:=types.ParsePacketMetadata(метадані)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
//Якщо аналізовані метадані є нульовими, це означає, що логіка пересилання відсутня
//Передайте пакет до наступного проміжного програмного забезпечення
ifpacketForwardMetadata==nil{
returnim.app.OnRecvPacket(ctx,пакет,ретранслятор)
}
//Змініть пакетні дані, замінивши поле метаданих JSON на адресу отримання
//щоб дозволити пакету продовжувати рух вниз по стеку
новіДані:=дані
newData.Receiver=packetForwardMetadata.Receiver
bz,err:=transfertypes.ModuleCdc.MarshalJSON(&newData)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
newPacket := пакет
newPacket.Data=bz
//Передайте новий пакет у стек середнього програмного забезпечення
ack:=im.app.OnRecvPacket(ctx,newPacket,relayer)
if!ack.Success(){
returnack
}
autopilotParams:=im.keeper.GetParams(ctx)
//Якщо передача була успішною, то маршрут до відповідного модуля, якщо це можливо
switchroutingInfo:=packetForwardMetadata.RoutingInfo.(тип){
casetypes.StakeibcPacketMetadata:
[...]
casetypes.ClaimPacketMetadata:
//Якщо стверджувати, що маршрутизація неактивна (але пакет містить інформацію про маршрутизацію в пам'ятці) повертає помилку
[..]
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
за замовчуванням:
returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))
}
}
Якщо включені метадані вказують на те, що вхідна передача є вимогою airdrop, модуль викликає функцію TryUpdateAirdropClaim:
func(kKeeper)TryUpdateAirdropClaim(
ctxsdk.Контекст,
datatransfertypes.FungibleTokenPacketData,
packetMetadatatypes.ClaimPacketMetadata,
)помилка{
[..]
//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("оновлення_аеродрому%s(orig%s)до%sдляаеродрому%s",
senderStrideAddress,data.Sender,newStrideAddress,airdropId))
returnk.claimKeeper.UpdateAirdropAddress(ctx,senderStrideAddress,newStrideAddress,airdropId)
}
Ця функція перетворює адресу відправника пакета IBC на адресу Stride під назвою senderStrideAddress і витягує airdropId і нову адресу airdrop newStrideAddress із метаданих пакета. Потім він викликає UpdateAirdropAddress, щоб оновити відкритий ClaimRecord, який відповідає комбінації senderStrideAddress і airdropId до нової адреси.
Завдяки оновленню ClaimRecord newStrideAddress тепер може вимагати еірдроп. Важливо зазначити, що цей механізм оновлення захищений лише адресою відправника, зазначеною в пакеті IBC. Stride не виконує жодних інших перевірок, щоб переконатися, що оновлення airdrops ініціювали реальні одержувачі.
Щоб зрозуміти, чому це серйозна вразливість, нам потрібно ближче розглянути IBC, протокол зв’язку між блоками.
IBC Security
IBC — це легкий міжланцюговий механізм зв’язку на основі клієнта. Подібно до класичних мережевих протоколів, основний модуль IBC абстрагує багато низькорівневих деталей, що дозволяє розробникам легко створювати власні інтеграції на його основі. Підключення одного ланцюга з підтримкою IBC (ланцюга A) до іншого ланцюга з підтримкою IBC (ланцюга B) виглядає приблизно так:
СтвореноsolomachineclientonIBCenabledchain[ClientID=06-solomachine-6]
Створено tendermintclientonsolomachine[ClientID=07-tendermint-M48f]
Ініціалізоване з’єднання на IBCenabledchain[ConnectionID=connection-4]
Ініціалізоване з'єднання на соломашині[ConnectionID=connection-Kinb]
Підтверджене підключення в ланцюжку з увімкненим IBC[ConnectionID=connection-4]
Підтверджене підключення на одній машині[ConnectionID=connection-Kinb]
Ініціалізований канал IBCenabledchain[ChannelID=channel-0]
Ініціалізований каналsolomachine[ChannelID=channel-wwl6]
Підтверджений канал IBCenabledchain[ChannelID=channel-0]
Підтверджено каналsolomachine[ChannelID=channel-wwl6]
Підключення встановлено!
На першому кроці легкий клієнт IBC ланцюга A створюється в ланцюзі B. Клієнт IBC унікально ідентифікується за його ідентифікатором клієнта, який використовується для відстеження та перевірки стану віддаленого ланцюга. Після того, як клієнти створені, вони можуть підключатися через з’єднання, яке ініціюється чотиристороннім рукостисканням. Це створює ConnectionEnd у ланцюжку A з легкими клієнтами ланцюга B на A, а інший у ланцюжку B з легкими клієнтами ланцюга A на B. З’єднання після створення є постійними та шифруються двома легкими клієнтами.
Спілкування через підключення також поділяється на різні канали. Канали ідентифікуються основним підключенням і портами джерела та призначення. Кожен порт ідентифікує модуль у відповідному ланцюжку, підключеному через IBC. ChannelEnd, пов’язаний зі з’єднанням, створюється в обох ланцюгах і ідентифікується ідентифікатором каналу. Тепер дані можна передавати між двома ланцюгами через встановлений канал.
Важливо пам’ятати, що IBC за замовчуванням є протоколом без дозволу. Це означає, що будь-хто може з’єднати будь-які два ланцюги з підтримкою IBC без попереднього дозволу чи схвалення. Насправді IBC підтримує так звані Solo Machines [7] Стандартно клієнт представляє не блокчейн, а окремий хост або машину. Оскільки вміст пакету IBC повністю контролюється відправником (зазвичай модулем джерела в ланцюжку джерел), модулям, які виконують привілейовані операції над вхідними пакетами IBC, завжди потрібно перевіряти, чи повідомлення надійшло з надійного каналу.
Уразливості
Однак, що стосується Stride, перевірка каналів відсутня в модулі x/autopilot. Код припускає, що пакети ICS-20 IBC з конкретною адресою відправника можуть надсилатися лише тими, хто контролює цю адресу. Це вірно, якщо ми розглядаємо лише транспортні модулі в надійних партнерських ланцюгах, як-от EVMOS, але зловмисники можуть просто надсилати повністю контрольовані пакетні дані IBC, щоб використовувати шкідливі клієнти IBC під своїм контролем. Використання цієї вразливості відносно просте:
Виправлення помилок
Отримавши наш швидкий звіт, учасник Stride швидко зняв усі кошти з гаманця торговельного посередника Airdrop, щоб переконатися, що кошти не знаходяться під загрозою. Реалізовано довгострокове виправлення, щоб гарантувати, що пакети оновлення адреси IBC airdrop надходять через правильний надійний канал IBC.
на завершення
Сильна підтримка міжланцюгового зв’язку через IBC є унікальною перевагою екосистеми Cosmos. Хоча IBC побудовано на надійних криптографічних примітивах, безпечна інтеграція з ним вимагає хорошого розуміння базової моделі довіри. Розробники, які створюють поверхню IBC, та інженери з безпеки, які перевіряють інтеграцію IBC, повинні уважно перевірити поверхню атаки, яка піддається зловмисним клієнтам або каналам IBC. Ми хотіли б подякувати учасникам Stride за професійне поводження та швидку відповідь на цю проблему.