Разработчики, работающие на основе IBC, и инженеры по безопасности, проверяющие интеграцию IBC, должны тщательно изучить поверхность атаки, доступную для вредоносных клиентов или каналов IBC.
Автор: Уилл
Предисловие: В этой статье описывается уязвимость, обнаруженная Jump Crypto в программе раздачи Stride: Stride — это сеть Cosmos, используемая для ликвидных ставок в экосистеме Cosmos. Эта проблема может позволить злоумышленнику украсть все невостребованные аирдропы на Stride. На момент обнаружения более 1,6 миллиона STRD (что эквивалентно примерно 4 миллионам долларов США) находились под угрозой. Jump в частном порядке сообщил об уязвимости участникам Stride, и с тех пор проблема была устранена, и в результате наших усилий не было никаких вредоносных эксплойтов. Оригинальный адрес ссылки [1]
Аирдроп на Stride
Stride регулярно проводит крупные раздачи своего нативного токена [$STRD], чтобы стимулировать сетевую активность и децентрализовать управление среди широкой группы сторон. Код для распределения и получения аирдропов находится по адресу x/claim. [2] реализован в модуле. Распределение Airdrop осуществляется через LoadAllocationData [3] определяется функцией, которая загружает файл распределения, содержащий адреса и распределения airdrop. Для большинства аирдропов загруженные адреса описывают пользователей в других цепочках Cosmos, таких как Osmosis или Juno, поэтому код сначала преобразует их в адреса Stride с помощью функции utils.ConvertAddressToStrideAddress.
Для каждой учетной записи в аирдропе модуль создает ClaimRecord. [4] , который содержит идентификатор аирдропа для конкретного аирдропа, преобразованный адрес и количество токенов, выделенных пользователю. После создания ClaimRecord пользователь с соответствующим адресом Stride может отправить MsgClaimFreeAmount в цепочку. [5] Приходите и получите их аирдроп.
Однако эта реализация не работала во время недавнего раздачи EVMOS, потому что функция utils.ConvertAddressToStrideAddress сопоставляет адреса Evmos с адресами Stride, которые недоступны. Это связано с тем, что адреса EVMOS получены с использованием монеты типа 60, а адреса Stride получены с использованием монеты типа 118.
Чтобы затронутые пользователи по-прежнему могли претендовать на раздачу, команда добавила возможность обновлять целевой адрес невостребованной записи ClaimRecord с помощью межсетевого сообщения IBC из соответствующей учетной записи EVMOS. Этот механизм обновления реализован как часть модуля x/autopilot. х/автопилот [6] Перехватить входящую передачу IBC ICS-20 и попытаться извлечь специфичные для Stride инструкции из его памятки или поля получателя (поле получателя удваивается как поле памятки в версиях IBC до v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Контекст,
типы пакетов.Пакет,
relayersdk.AccAddress,
)ibcexported.Acknowledgement{
//ПРИМЕЧАНИЕ: подтверждение будет записано синхронно во время обработки IBC.
Функция преобразует адрес пакета IBC отправителя в адрес Stride с именем senderStrideAddress и извлекает идентификатор airdropId и новый адрес airdrop newStrideAddress из метаданных пакета. Затем он вызывает UpdateAirdropAddress, чтобы обновить открытую ClaimRecord, которая соответствует комбинации senderStrideAddress и airdropId новому адресу.
С обновлением ClaimRecord newStrideAddress теперь может претендовать на аирдроп. Важно отметить, что этот механизм обновления защищен только адресом отправителя, указанным в пакете IBC. Stride не выполняет никакой другой проверки, чтобы гарантировать, что обновления для раздачи были инициированы реальными получателями.
Чтобы понять, почему это серьезная уязвимость, нам нужно поближе познакомиться с IBC, протоколом обмена данными между блокчейнами.
Безопасность IBC
IBC — это легкий механизм межсетевой связи на основе клиента. Подобно классическим сетевым протоколам, базовый модуль IBC абстрагируется от многих низкоуровневых деталей, что позволяет разработчикам легко создавать на их основе свои собственные интеграции. Подключение одной цепочки с поддержкой IBC (цепочка A) к другой цепочке с поддержкой IBC (цепочка B) выглядит примерно так:
Инициализированный канал в цепочке IBCenabled[ChannelID=channel-0]
Инициализированный канал на соломашине[ChannelID=channel-wwl6]
Подтвержденный канал в IBCenabledchain[ChannelID=channel-0]
Подтвержденный канал на соломашине[ChannelID=channel-wwl6]
Соединение установлено!
На первом этапе легкий клиент IBC цепочки A создается в цепочке B и наоборот.Клиент IBC однозначно идентифицируется своим идентификатором клиента, который используется для отслеживания и проверки состояния удаленной цепочки. После того, как клиенты созданы, они могут подключаться через соединение, которое инициируется четырехэтапным рукопожатием. Это создает ConnectionEnd в цепочке A с легкими клиентами цепочки B на A и еще один в цепочке B с легкими клиентами цепочки A на B. Соединения, однажды созданные, являются постоянными и шифруются двумя легкими клиентами.
Связь по соединениям также делится на разные каналы. Каналы идентифицируются базовым соединением, а также исходным и целевым портами. Каждый порт идентифицирует модуль в соответствующей цепочке, подключенной через IBC. ChannelEnd, связанный с Connection, создается в обеих цепочках и идентифицируется идентификатором канала. Теперь данные могут передаваться между двумя цепочками по установленному каналу.
Важно помнить, что 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, чтобы активировать автопилот и обновить адрес раздачи на учетную запись Stride, контролируемую злоумышленником.
Украсть аирдроп, отправив MsgClaimFreeAmount в модуль x/claim
Исправление ошибок
Получив наш оперативный отчет, участник Stride быстро вывел все средства из кошелька реселлера Airdrop, чтобы гарантировать, что средства не подверглись риску. Реализовано долгосрочное исправление, чтобы гарантировать, что пакеты обновления адреса IBC для раздачи по воздуху поступают по правильному доверенному каналу IBC.
в заключение
Мощная поддержка межсетевого взаимодействия через IBC — уникальное преимущество экосистемы Cosmos. Хотя IBC построен на надежных криптографических примитивах, безопасная интеграция с ним требует хорошего понимания базовой модели доверия. Разработчики, работающие на основе IBC, и инженеры по безопасности, проверяющие интеграцию IBC, должны тщательно изучить поверхность атаки, доступную для вредоносных клиентов или каналов IBC. Мы хотели бы поблагодарить участников Stride за их профессиональное решение и быстрое реагирование на эту проблему.
Внешняя ссылка WeChat
[1] Оригинальный адрес ссылки:
[2] х/претензия:
[3] Лоадаллокейшндата:
[4] ПретензияЗапись:
[5] MsgClaimFreeAmount:
[6] х/автопилот:
[7] Сольные машины:
Посмотреть Оригинал
Содержание носит исключительно справочный характер и не является предложением или офертой. Консультации по инвестициям, налогообложению или юридическим вопросам не предоставляются. Более подробную информацию о рисках см. в разделе «Дисклеймер».
Анализ уязвимости Stride Airdrop
Автор: Уилл
Предисловие: В этой статье описывается уязвимость, обнаруженная Jump Crypto в программе раздачи Stride: Stride — это сеть Cosmos, используемая для ликвидных ставок в экосистеме Cosmos. Эта проблема может позволить злоумышленнику украсть все невостребованные аирдропы на Stride. На момент обнаружения более 1,6 миллиона STRD (что эквивалентно примерно 4 миллионам долларов США) находились под угрозой. Jump в частном порядке сообщил об уязвимости участникам Stride, и с тех пор проблема была устранена, и в результате наших усилий не было никаких вредоносных эксплойтов. Оригинальный адрес ссылки [1]
Аирдроп на Stride
Stride регулярно проводит крупные раздачи своего нативного токена [$STRD], чтобы стимулировать сетевую активность и децентрализовать управление среди широкой группы сторон. Код для распределения и получения аирдропов находится по адресу x/claim. [2] реализован в модуле. Распределение Airdrop осуществляется через LoadAllocationData [3] определяется функцией, которая загружает файл распределения, содержащий адреса и распределения airdrop. Для большинства аирдропов загруженные адреса описывают пользователей в других цепочках Cosmos, таких как Osmosis или Juno, поэтому код сначала преобразует их в адреса Stride с помощью функции utils.ConvertAddressToStrideAddress.
Для каждой учетной записи в аирдропе модуль создает ClaimRecord. [4] , который содержит идентификатор аирдропа для конкретного аирдропа, преобразованный адрес и количество токенов, выделенных пользователю. После создания ClaimRecord пользователь с соответствующим адресом Stride может отправить MsgClaimFreeAmount в цепочку. [5] Приходите и получите их аирдроп.
Однако эта реализация не работала во время недавнего раздачи EVMOS, потому что функция utils.ConvertAddressToStrideAddress сопоставляет адреса Evmos с адресами Stride, которые недоступны. Это связано с тем, что адреса EVMOS получены с использованием монеты типа 60, а адреса Stride получены с использованием монеты типа 118.
Чтобы затронутые пользователи по-прежнему могли претендовать на раздачу, команда добавила возможность обновлять целевой адрес невостребованной записи ClaimRecord с помощью межсетевого сообщения IBC из соответствующей учетной записи EVMOS. Этот механизм обновления реализован как часть модуля x/autopilot. х/автопилот [6] Перехватить входящую передачу IBC ICS-20 и попытаться извлечь специфичные для Stride инструкции из его памятки или поля получателя (поле получателя удваивается как поле памятки в версиях IBC до v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Контекст,
типы пакетов.Пакет,
relayersdk.AccAddress,
)ibcexported.Acknowledgement{
//ПРИМЕЧАНИЕ: подтверждение будет записано синхронно во время обработки IBC.
datatransfertypes.FungibleTokenPacketData
iferr:=transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{
returnchanneltypes.NewErrorAcknowledgement (ошибка)
}
[..]
//ibc-gov5 имеет поле Memo, в котором может храниться информация о переадресации
//Для более старой версии ibc-go данные должны быть сохранены в поле получателя
строка метаданных
ifdata.Memo!=""{//ibc-gov5+
metadata=data.Memo
}else{//beforeibc-gov5
метаданные = данные. Получатель
}
[..]
//анализировать любую информацию о пересылке
packageForwardMetadata, err:=types.ParsePacketMetadata(метаданные)
если! = ноль {
returnchanneltypes.NewErrorAcknowledgement (ошибка)
}
//Если проанализированные метаданные равны нулю, это означает, что логика переадресации отсутствует
// Передаем пакет следующему промежуточному программному обеспечению
ifpacketForwardMetadata==nil{
returnim.app.OnRecvPacket (ctx, пакет, ретранслятор)
}
//Изменяем данные пакета, заменив поле метаданных JSON на адрес получения
//чтобы позволить пакету двигаться вниз по стеку
новые данные: = данные
newData.Receiver=packetForwardMetadata.Receiver
bz,err:=transfertypes.ModuleCdc.MarshalJSON(&newData)
если! = ноль {
returnchanneltypes.NewErrorAcknowledgement (ошибка)
}
новый пакет := пакет
новыйПакет.Данные=bz
// Сначала передаем новый пакет в стек ПО промежуточного слоя
ack:=im.app.OnRecvPacket(ctx,newPacket,relayer)
если!Подтвердить.Успех(){
возвращение
}
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("Ошибка обновления заявления о воздушном сбросе от автопилота для%s:%s",newData.Sender,err.Error()))
returnchanneltypes.NewErrorAcknowledgement (ошибка)
}
возвращение
по умолчанию:
returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))
}
}
Если включенные метаданные указывают на то, что входящий перевод является претензией на раздачу, модуль вызывает функцию TryUpdateAirdropClaim:
func(kKeeper)TryUpdateAirdropClaim(
ctxsdk.Контекст,
datatransfertypes.FungibleTokenPacketData,
packageMetadatatypes.ClaimPacketMetadata,
)ошибка{
[..]
//грабрелевантадреса
senderStrideAddress:=utils.ConvertAddressToStrideAddress(data.Sender)
ifsenderStrideAddress==""{
returnerrorsmod.Wrapf(sdkerrors.ErrInvalidAddress,fmt.Sprintf("invalidenderaddress(%s)",data.Sender))
}
новыйStrideAddress:=packetMetadata.StrideAddress
//обновляем аирдроп
airdropId:=packetMetadata.AirdropId
k.Logger(ctx).Info(fmt.Sprintf("обновление адреса airdrop%s(orig%s) до%sforairdrop%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 не выполняет никакой другой проверки, чтобы гарантировать, что обновления для раздачи были инициированы реальными получателями.
Чтобы понять, почему это серьезная уязвимость, нам нужно поближе познакомиться с IBC, протоколом обмена данными между блокчейнами.
Безопасность IBC
IBC — это легкий механизм межсетевой связи на основе клиента. Подобно классическим сетевым протоколам, базовый модуль IBC абстрагируется от многих низкоуровневых деталей, что позволяет разработчикам легко создавать на их основе свои собственные интеграции. Подключение одной цепочки с поддержкой IBC (цепочка A) к другой цепочке с поддержкой IBC (цепочка B) выглядит примерно так:
CreatedsolomachineclientonIBCenabledchain[ClientID=06-solomachine-6]
Созданtendermintclientonsolomachine[ClientID=07-tendermint-M48f]
InitializedconnectiononIBCenabledchain[ConnectionID=connection-4]
Initializedconnectiononsolomachine[ConnectionID=connection-Kinb]
Подтвержденное подключение по цепочке IBCenabled[ConnectionID=connection-4]
Confirmedconnectiononsolomachine[ConnectionID=connection-Kinb]
Инициализированный канал в цепочке IBCenabled[ChannelID=channel-0]
Инициализированный канал на соломашине[ChannelID=channel-wwl6]
Подтвержденный канал в IBCenabledchain[ChannelID=channel-0]
Подтвержденный канал на соломашине[ChannelID=channel-wwl6]
Соединение установлено!
На первом этапе легкий клиент IBC цепочки A создается в цепочке B и наоборот.Клиент IBC однозначно идентифицируется своим идентификатором клиента, который используется для отслеживания и проверки состояния удаленной цепочки. После того, как клиенты созданы, они могут подключаться через соединение, которое инициируется четырехэтапным рукопожатием. Это создает ConnectionEnd в цепочке A с легкими клиентами цепочки B на A и еще один в цепочке B с легкими клиентами цепочки A на B. Соединения, однажды созданные, являются постоянными и шифруются двумя легкими клиентами.
Связь по соединениям также делится на разные каналы. Каналы идентифицируются базовым соединением, а также исходным и целевым портами. Каждый порт идентифицирует модуль в соответствующей цепочке, подключенной через IBC. ChannelEnd, связанный с Connection, создается в обеих цепочках и идентифицируется идентификатором канала. Теперь данные могут передаваться между двумя цепочками по установленному каналу.
Важно помнить, что 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 за их профессиональное решение и быстрое реагирование на эту проблему.