Os desenvolvedores baseados no IBC e os engenheiros de segurança que revisam as integrações do IBC devem revisar cuidadosamente a superfície de ataque exposta a clientes ou canais IBC maliciosos.
Escrito por: Will
Prefácio: Este artigo descreve uma vulnerabilidade que a Jump Crypto descobriu no programa de airdrop Stride: Stride é uma cadeia Cosmos usada para apostas líquidas no ecossistema Cosmos. Esse problema pode permitir que um invasor roube todos os airdrops não reclamados no Stride. No momento da descoberta, mais de 1,6 milhão de STRDs (equivalente a aproximadamente US$ 4 milhões) estavam em risco. A Jump relatou em particular a vulnerabilidade aos colaboradores do Stride, e o problema foi corrigido desde então, sem nenhuma exploração maliciosa ocorrendo como resultado de nossos esforços. Endereço do link original [1]
Airdrop em Stride
A Stride realiza regularmente grandes airdrops de seu token nativo [$STRD] para incentivar a atividade da rede e descentralizar a governança em um amplo grupo de partes. O código para alocar e reivindicar airdrops está em x/claim [2] implementado no módulo. A alocação de airdrop é por meio de LoadAllocationData [3] definido por uma função que carrega um arquivo de alocação contendo endereços e alocações de airdrop. Para a maioria dos airdrops, os endereços carregados descrevem usuários em outras cadeias do Cosmos, como Osmosis ou Juno, portanto, o código primeiro os converte em endereços Stride usando a função utils.ConvertAddressToStrideAddress.
Para cada conta no airdrop, o módulo cria um ClaimRecord [4] , que contém o identificador de airdrop para um determinado airdrop, o endereço convertido e a quantidade de tokens alocados para o usuário. Depois de criar um ClaimRecord, um usuário com o endereço Stride correspondente pode enviar um MsgClaimFreeAmount para a cadeia [5] Venha e reivindique seu lançamento aéreo.
No entanto, esta implementação não funcionou durante o airdrop recente do EVMOS porque a função utils.ConvertAddressToStrideAddress mapeia endereços Evmos para endereços Stride que não são acessíveis. Isso ocorre porque os endereços EVMOS são derivados usando o tipo de moeda 60, enquanto os endereços Stride são derivados usando o tipo de moeda 118.
Para que os usuários afetados ainda possam reivindicar o airdrop, a equipe adicionou a capacidade de atualizar o endereço de destino de um ClaimRecord não reivindicado por meio de uma mensagem IBC de cadeia cruzada da conta EVMOS correspondente. Este mecanismo de atualização é implementado como parte do módulo x/autopilot. x/piloto automático [6] Intercepte uma transmissão IBC ICS-20 de entrada e tente extrair instruções específicas do Stride de seu memo ou campo receptor (o campo receptor funciona como o campo memo nas versões IBC anteriores à v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Context,
tipos de canal de pacotes.Pacote,
relayersdk.AccAddress,
)ibcexportado.Agradecimento{
//NOTA: o reconhecimento será gravado de forma síncrona durante a operação do IBChandler.
A função converte o endereço do pacote IBC do remetente em um endereço Stride chamado senderStrideAddress e extrai o airdropId e o novo endereço airdrop newStrideAddress dos metadados do pacote. Em seguida, ele chama UpdateAirdropAddress para atualizar um ClaimRecord aberto que corresponda à combinação de senderStrideAddress e airdropId para o novo endereço.
Com a atualização de ClaimRecord, newStrideAddress agora pode reivindicar o airdrop. É importante observar que esse mecanismo de atualização é protegido apenas pelo endereço do remetente especificado no pacote IBC. O Stride não realiza nenhuma outra verificação para garantir que as atualizações dos airdrops foram acionadas por destinatários reais.
Para entender por que essa é uma vulnerabilidade séria, precisamos examinar mais de perto o IBC, o protocolo de comunicação inter-blockchain.
Segurança IBC
O IBC é um mecanismo de comunicação de cadeia cruzada baseado em cliente leve. Semelhante aos protocolos de rede clássicos, o módulo principal IBC abstrai muitos detalhes de baixo nível, permitindo que os desenvolvedores criem facilmente suas próprias integrações sobre ele. Conectar uma cadeia habilitada para IBC (cadeia A) a outra corrente habilitada para IBC (cadeia B) é mais ou menos assim:
Na primeira etapa, um cliente leve IBC da cadeia A é criado na cadeia B e vice-versa.O cliente IBC é identificado exclusivamente por seu ID de cliente, que é usado para rastrear e verificar o estado da cadeia remota. Depois que os clientes são criados, eles podem se conectar por meio de uma conexão, que é iniciada com um handshake de quatro vias. Isso cria um ConnectionEnd na cadeia A com clientes leves da cadeia B em A e outro na cadeia B com clientes leves da cadeia A em B. As conexões, uma vez criadas, são persistentes e criptografadas por dois clientes leves.
A comunicação por conexões também é dividida em diferentes canais. Os canais são identificados pela conexão subjacente e pelas portas de origem e destino. Cada porta identifica um módulo na cadeia correspondente conectada através do IBC. O ChannelEnd associado à Connection é criado em ambas as chains e identificado pelo channel-id. Os dados agora podem ser transferidos entre as duas cadeias através do canal estabelecido.
É importante lembrar que o IBC é um protocolo sem permissão por padrão. Isso significa que qualquer pessoa pode conectar quaisquer duas cadeias habilitadas para IBC sem autorização ou aprovação prévia. Na verdade, o IBC suporta as chamadas Solo Machines [7] Padrão, um cliente não representa um blockchain, mas um único host ou máquina. Como o conteúdo do pacote IBC é totalmente controlado pelo remetente (geralmente o módulo de origem na cadeia de origem), os módulos que executam operações privilegiadas nos pacotes IBC recebidos sempre precisam verificar se a mensagem veio de um canal confiável.
Vulnerabilidades
No entanto, no que diz respeito ao Stride, falta a verificação de canal no módulo x/piloto automático. O código assume que os pacotes ICS-20 IBC com um endereço de remetente específico só podem ser enviados por alguém que tenha controle sobre esse endereço. Isso é verdade se considerarmos apenas os módulos de transporte em cadeias de parceiros confiáveis, como EVMOS, mas um invasor pode simplesmente enviar dados de pacotes IBC totalmente controlados para usar um cliente IBC malicioso sob seu controle. A exploração dessa vulnerabilidade é relativamente simples:
Crie um cliente IBC malicioso
Usando o cliente malicioso Craft para criar um canal IBC para o módulo de transporte Stride IBC,
E envie uma transferência IBC maliciosa usando o endereço dos ClaimRecords não reclamados como o campo De. Use o campo de memorando ClaimMetadata para acionar o piloto automático e atualizar o endereço de airdrop para uma conta Stride controlada pelo invasor.
Roube o airdrop enviando MsgClaimFreeAmount para o módulo x/claim
Correções de bugs
Ao receber nosso relatório imediato, o colaborador do Stride retirou rapidamente todos os fundos da carteira do revendedor Airdrop para garantir que nenhum dinheiro estivesse em risco. Correção de longo prazo implementada para garantir que os pacotes de atualização de endereço de airdrop IBC cheguem através do canal IBC confiável correto.
para concluir
O forte suporte para comunicação entre cadeias por meio do IBC é uma vantagem exclusiva do ecossistema Cosmos. Embora o IBC seja construído em primitivas criptográficas sólidas, a integração segura com ele requer um bom entendimento do modelo de confiança subjacente. Os desenvolvedores baseados no IBC e os engenheiros de segurança que revisam as integrações do IBC devem revisar cuidadosamente a superfície de ataque exposta a clientes ou canais IBC maliciosos. Gostaríamos de agradecer aos colaboradores do Stride por seu tratamento profissional e resposta rápida a este problema.
Link externo do WeChat
[1] Endereço do link original:
[2] x/reivindicação:
[3] LoadAllocationData:
[4] Registro de reivindicação:
[5] MsgClaimFreeAmount:
[6] x/piloto automático:
[7] Máquinas solo:
Ver original
O conteúdo serve apenas de referência e não constitui uma solicitação ou oferta. Não é prestado qualquer aconselhamento em matéria de investimento, fiscal ou jurídica. Consulte a Declaração de exoneração de responsabilidade para obter mais informações sobre os riscos.
Stride Análise de Vulnerabilidade Airdrop
Escrito por: Will
Prefácio: Este artigo descreve uma vulnerabilidade que a Jump Crypto descobriu no programa de airdrop Stride: Stride é uma cadeia Cosmos usada para apostas líquidas no ecossistema Cosmos. Esse problema pode permitir que um invasor roube todos os airdrops não reclamados no Stride. No momento da descoberta, mais de 1,6 milhão de STRDs (equivalente a aproximadamente US$ 4 milhões) estavam em risco. A Jump relatou em particular a vulnerabilidade aos colaboradores do Stride, e o problema foi corrigido desde então, sem nenhuma exploração maliciosa ocorrendo como resultado de nossos esforços. Endereço do link original [1]
Airdrop em Stride
A Stride realiza regularmente grandes airdrops de seu token nativo [$STRD] para incentivar a atividade da rede e descentralizar a governança em um amplo grupo de partes. O código para alocar e reivindicar airdrops está em x/claim [2] implementado no módulo. A alocação de airdrop é por meio de LoadAllocationData [3] definido por uma função que carrega um arquivo de alocação contendo endereços e alocações de airdrop. Para a maioria dos airdrops, os endereços carregados descrevem usuários em outras cadeias do Cosmos, como Osmosis ou Juno, portanto, o código primeiro os converte em endereços Stride usando a função utils.ConvertAddressToStrideAddress.
Para cada conta no airdrop, o módulo cria um ClaimRecord [4] , que contém o identificador de airdrop para um determinado airdrop, o endereço convertido e a quantidade de tokens alocados para o usuário. Depois de criar um ClaimRecord, um usuário com o endereço Stride correspondente pode enviar um MsgClaimFreeAmount para a cadeia [5] Venha e reivindique seu lançamento aéreo.
No entanto, esta implementação não funcionou durante o airdrop recente do EVMOS porque a função utils.ConvertAddressToStrideAddress mapeia endereços Evmos para endereços Stride que não são acessíveis. Isso ocorre porque os endereços EVMOS são derivados usando o tipo de moeda 60, enquanto os endereços Stride são derivados usando o tipo de moeda 118.
Para que os usuários afetados ainda possam reivindicar o airdrop, a equipe adicionou a capacidade de atualizar o endereço de destino de um ClaimRecord não reivindicado por meio de uma mensagem IBC de cadeia cruzada da conta EVMOS correspondente. Este mecanismo de atualização é implementado como parte do módulo x/autopilot. x/piloto automático [6] Intercepte uma transmissão IBC ICS-20 de entrada e tente extrair instruções específicas do Stride de seu memo ou campo receptor (o campo receptor funciona como o campo memo nas versões IBC anteriores à v5):
func(imIBCModule)OnRecvPacket(
ctxsdk.Context,
tipos de canal de pacotes.Pacote,
relayersdk.AccAddress,
)ibcexportado.Agradecimento{
//NOTA: o reconhecimento será gravado de forma síncrona durante a operação do IBChandler.
datatransfertypes.FungibleTokenPacketData
iferr:=transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
[..]
//ibc-gov5hasaMemofieldthatcanstoreforwardinginfo
//Para versões mais antigas do ibc-go,osdadosdevemserarmazenadosnocamporeceptor
cadeia de metadados
ifdata.Memo!=""{//ibc-gov5+
metadata=data.Memo
}else{//beforeibc-gov5
metadados=dados.Receptor
}
[..]
// analisa qualquer informação de encaminhamento
packetForwardMetadata,err:=types.ParsePacketMetadata(metadados)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
// Se os metadados analisados forem nulos, isso significa que não há lógica de encaminhamento
//Passe o pacote para o próximo middleware
ifpacketForwardMetadata==nil{
returnim.app.OnRecvPacket(ctx,packet,relayer)
}
//Modificar os dados do pacote substituindo o campo de metadados JSON por um endereço de recebimento
//para permitir que o pacote continue descendo a pilha
novosDados:=dados
newData.Receiver=packetForwardMetadata.Receiver
bz,err:=transfertypes.ModuleCdc.MarshalJSON(&newData)
iferr!=nil{
returnchanneltypes.NewErrorAcknowledgement(err)
}
novoPacote := pacote
newPacket.Data=bz
// Passe primeiro o novo pacote pela pilha de middleware
ack:=im.app.OnRecvPacket(ctx,newPacket,relayer)
if!ack.Success(){
retorno
}
autopilotParams:=im.keeper.GetParams(ctx)
//Se a transferência foi bem-sucedida, rotear para o módulo correspondente, se aplicável
switchroutingInfo:=packetForwardMetadata.RoutingInfo.(type){
casetypes.StakeibcPacketMetadata:
[...]
casetypes.ClaimPacketMetadata:
//Se a reivindicação de roteamento estiver inativa (mas o pacote tiver informações de roteamento no memorando), retorne um erro de erro
[..]
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("Erro ao atualizar solicitação de airdrop do piloto automático para%s:%s",newData.Sender,err.Error()))
returnchanneltypes.NewErrorAcknowledgement(err)
}
retorno
padrão:
returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))
}
}
Se os metadados incluídos indicarem que a transferência de entrada é uma solicitação de airdrop, o módulo chamará a função TryUpdateAirdropClaim:
func(kKeeper)TryUpdateAirdropClaim(
ctxsdk.Context,
datatransfertypes.FungibleTokenPacketData,
packetMetadatatypes.ClaimPacketMetadata,
)erro{
[..]
// pega endereços relevantes
senderStrideAddress:=utils.ConvertAddressToStrideAddress(data.Sender)
ifsenderStrideAddress==""{
returnerrorsmod.Wrapf(sdkerrors.ErrInvalidAddress,fmt.Sprintf("invalidsenderaddress(%s)",data.Sender))
}
newStrideAddress:=packetMetadata.StrideAddress
//atualiza o airdrop
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)
}
A função converte o endereço do pacote IBC do remetente em um endereço Stride chamado senderStrideAddress e extrai o airdropId e o novo endereço airdrop newStrideAddress dos metadados do pacote. Em seguida, ele chama UpdateAirdropAddress para atualizar um ClaimRecord aberto que corresponda à combinação de senderStrideAddress e airdropId para o novo endereço.
Com a atualização de ClaimRecord, newStrideAddress agora pode reivindicar o airdrop. É importante observar que esse mecanismo de atualização é protegido apenas pelo endereço do remetente especificado no pacote IBC. O Stride não realiza nenhuma outra verificação para garantir que as atualizações dos airdrops foram acionadas por destinatários reais.
Para entender por que essa é uma vulnerabilidade séria, precisamos examinar mais de perto o IBC, o protocolo de comunicação inter-blockchain.
Segurança IBC
O IBC é um mecanismo de comunicação de cadeia cruzada baseado em cliente leve. Semelhante aos protocolos de rede clássicos, o módulo principal IBC abstrai muitos detalhes de baixo nível, permitindo que os desenvolvedores criem facilmente suas próprias integrações sobre ele. Conectar uma cadeia habilitada para IBC (cadeia A) a outra corrente habilitada para IBC (cadeia B) é mais ou menos assim:
CriadosolomachineclientonIBCenabledchain[ClientID=06-solomachine-6]
Criadotendermintclientonsolomachine[ClientID=07-tendermint-M48f]
InitializedconnectiononIBCenabledchain[ConnectionID=connection-4]
Conexão inicializada na máquina solo[ConnectionID=conexão-Kinb]
ConexãoconfirmadanacadeiaativadaIBC[ConnectionID=conexão-4]
Conexão confirmada na máquina solo[ConnectionID=conexão-Kinb]
Canal inicializado em IBCenabledchain[ChannelID=canal-0]
Canalonsolomachine inicializado[ChannelID=channel-wwl6]
ConfirmedchannelonIBCenabledchain[ChannelID=channel-0]
Confirmedchannelonsolomachine[ChannelID=channel-wwl6]
Conexão estabelecida!
Na primeira etapa, um cliente leve IBC da cadeia A é criado na cadeia B e vice-versa.O cliente IBC é identificado exclusivamente por seu ID de cliente, que é usado para rastrear e verificar o estado da cadeia remota. Depois que os clientes são criados, eles podem se conectar por meio de uma conexão, que é iniciada com um handshake de quatro vias. Isso cria um ConnectionEnd na cadeia A com clientes leves da cadeia B em A e outro na cadeia B com clientes leves da cadeia A em B. As conexões, uma vez criadas, são persistentes e criptografadas por dois clientes leves.
A comunicação por conexões também é dividida em diferentes canais. Os canais são identificados pela conexão subjacente e pelas portas de origem e destino. Cada porta identifica um módulo na cadeia correspondente conectada através do IBC. O ChannelEnd associado à Connection é criado em ambas as chains e identificado pelo channel-id. Os dados agora podem ser transferidos entre as duas cadeias através do canal estabelecido.
É importante lembrar que o IBC é um protocolo sem permissão por padrão. Isso significa que qualquer pessoa pode conectar quaisquer duas cadeias habilitadas para IBC sem autorização ou aprovação prévia. Na verdade, o IBC suporta as chamadas Solo Machines [7] Padrão, um cliente não representa um blockchain, mas um único host ou máquina. Como o conteúdo do pacote IBC é totalmente controlado pelo remetente (geralmente o módulo de origem na cadeia de origem), os módulos que executam operações privilegiadas nos pacotes IBC recebidos sempre precisam verificar se a mensagem veio de um canal confiável.
Vulnerabilidades
No entanto, no que diz respeito ao Stride, falta a verificação de canal no módulo x/piloto automático. O código assume que os pacotes ICS-20 IBC com um endereço de remetente específico só podem ser enviados por alguém que tenha controle sobre esse endereço. Isso é verdade se considerarmos apenas os módulos de transporte em cadeias de parceiros confiáveis, como EVMOS, mas um invasor pode simplesmente enviar dados de pacotes IBC totalmente controlados para usar um cliente IBC malicioso sob seu controle. A exploração dessa vulnerabilidade é relativamente simples:
Correções de bugs
Ao receber nosso relatório imediato, o colaborador do Stride retirou rapidamente todos os fundos da carteira do revendedor Airdrop para garantir que nenhum dinheiro estivesse em risco. Correção de longo prazo implementada para garantir que os pacotes de atualização de endereço de airdrop IBC cheguem através do canal IBC confiável correto.
para concluir
O forte suporte para comunicação entre cadeias por meio do IBC é uma vantagem exclusiva do ecossistema Cosmos. Embora o IBC seja construído em primitivas criptográficas sólidas, a integração segura com ele requer um bom entendimento do modelo de confiança subjacente. Os desenvolvedores baseados no IBC e os engenheiros de segurança que revisam as integrações do IBC devem revisar cuidadosamente a superfície de ataque exposta a clientes ou canais IBC maliciosos. Gostaríamos de agradecer aos colaboradores do Stride por seu tratamento profissional e resposta rápida a este problema.