Análisis de vulnerabilidad de Stride Airdrop

Los desarrolladores que se basan en IBC y los ingenieros de seguridad que revisan las integraciones de IBC deben revisar cuidadosamente la superficie de ataque expuesta a clientes o canales IBC maliciosos.

Escrito por: Will

Prólogo: Este artículo describe una vulnerabilidad que Jump Crypto descubrió en el programa Stride airdrop: Stride es una cadena de Cosmos que se utiliza para hacer staking de líquidos en el ecosistema de Cosmos. Este problema podría permitir que un atacante robe todos los lanzamientos aéreos no reclamados en Stride. En el momento del descubrimiento, más de 1,6 millones de STRD (equivalentes a aproximadamente $4 millones) estaban en riesgo. Jump informó de forma privada la vulnerabilidad a los colaboradores de Stride y, desde entonces, el problema se solucionó, sin que se produjeran vulnerabilidades maliciosas como resultado de nuestros esfuerzos. Dirección del enlace original [1]

Lanzamiento aéreo en Stride

Stride realiza regularmente lanzamientos desde el aire de su token nativo [$STRD] para incentivar la actividad de la red y descentralizar la gobernanza en un amplio grupo de partes. El código para asignar y reclamar airdrops está en x/claim [2] implementado en el módulo. La asignación de Airdrop es a través de LoadAllocationData [3] definido por una función que carga un archivo de asignación que contiene direcciones y asignaciones de airdrop. Para la mayoría de los airdrops, las direcciones cargadas describen a los usuarios en otras cadenas de Cosmos, como Osmosis o Juno, por lo que el código primero las convierte en direcciones de Stride mediante la función utils.ConvertAddressToStrideAddress.

Para cada cuenta en el airdrop, el módulo crea un ClaimRecord [4] , que contiene el identificador de airdrop para un airdrop en particular, la dirección convertida y la cantidad de tokens asignados al usuario. Después de crear un ClaimRecord, un usuario con la dirección de Stride correspondiente puede enviar un MsgClaimFreeAmount a la cadena [5] Ven y reclama su lanzamiento aéreo.

Sin embargo, esta implementación no funcionó durante el airdrop reciente de EVMOS porque la función utils.ConvertAddressToStrideAddress asigna direcciones de Evmos a direcciones de Stride que no son accesibles. Esto se debe a que las direcciones EVMOS se derivan utilizando el tipo de moneda 60, mientras que las direcciones Stride se derivan utilizando el tipo de moneda 118.

Para que los usuarios afectados aún puedan reclamar el airdrop, el equipo agregó la capacidad de actualizar la dirección de destino de un ClaimRecord no reclamado a través de un mensaje IBC de cadena cruzada desde la cuenta EVMOS correspondiente. Este mecanismo de actualización se implementa como parte del módulo x/autopilot. x/piloto automático [6] Intercepte una transmisión IBC ICS-20 entrante e intente extraer instrucciones específicas de Stride de su memo o campo de receptor (el campo de receptor funciona como campo de memo en las versiones de IBC anteriores a v5):

func(imIBCModule)OnRecvPacket(

ctxsdk.Contexto,

paquetescanaltipos.paquete,

relayersdk.AccAddress,

)ibcexported.Reconocimiento{

//NOTA: el acuse de recibo se escribirá sincrónicamente durante la operación de manejo de IBC.

tipos de transferencia de datos.FungibleTokenPacketData

iferr:=tiposdetransferencia.ModuleCdc.UnmarshalJSON(packet.GetData(),&data);err!=nil{

returnchanneltypes.NewErrorAcknowledgement(err)

}

[..]

//ibc-gov5tieneuncampoMemoquepuedealmacenarinformacióndereenvío

//Paralaversiónanteriordefibc-go,losdatosdebenalmacenarseenelcampodelreceptor

cadena de metadatos

ifdata.Memo!=""{//ibc-gov5+

metadatos=datos.Memo

}else{//beforeibc-gov5

metadata=datos.Receptor

}

[..]

// analizar cualquier información de reenvío

paqueteForwardMetadata,err:=tipos.ParsePacketMetadata(metadatos)

ifr!=nil{

returnchanneltypes.NewErrorAcknowledgement(err)

}

//Si los metadatos analizados son nulos, eso significa que no hay lógica de reenvío

//Pasar el paquete al siguiente middleware

ifpacketForwardMetadata==nil{

returnim.app.OnRecvPacket (ctx, paquete, repetidor)

}

//ModificarlosdatosdelpaquetereemplazandoelcampodemetadatosJSONconunadirecciónderecepción

// para permitir que el paquete continúe en la pila

nuevosdatos:=datos

newData.Receiver=packetForwardMetadata.Receiver

bz,err:=tipos de transferencia.ModuleCdc.MarshalJSON(&newData)

ifr!=nil{

returnchanneltypes.NewErrorAcknowledgement(err)

}

nuevoPaquete := paquete

nuevoPaquete.Data=bz

//Pasar el nuevo paquete primero a la pila intermedia

ack:=im.app.OnRecvPacket(ctx,nuevoPaquete,retransmisor)

si!ack.Éxito(){

regreso

}

autopilotParams:=im.keeper.GetParams(ctx)

//Si la transferencia fue exitosa, luego enruta al módulo correspondiente, si corresponde

switchroutingInfo:=packetForwardMetadata.RoutingInfo.(tipo){

tipos de casos.StakeibcPacketMetadata:

[...]

tipos de casos.ClaimPacketMetadata:

//Si el enrutamiento de reclamación está inactivo (pero el paquete tenía información de enrutamiento en el memo) devuelve un error de reconocimiento

[..]

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("Error al actualizar la reclamación de airdrop del piloto automático para%s:%s",newData.Sender,err.Error()))

returnchanneltypes.NewErrorAcknowledgement(err)

}

regreso

por defecto:

returnchanneltypes.NewErrorAcknowledgement(errorsmod.Wrapf(types.ErrUnsupportedAutopilotRoute,"%T",routingInfo))

}

}

Si los metadatos incluidos indican que la transferencia entrante es un reclamo de airdrop, el módulo llama a la función TryUpdateAirdropClaim:

func(kKeeper)TryUpdateAirdropClaim(

ctxsdk.Contexto,

tipos de transferencia de datos.FungibleTokenPacketData,

tipos de paqueteMetadata.ClaimPacketMetadata,

)error{

[..]

//agarrar direcciones relevantes

senderStrideAddress:=utils.ConvertAddressToStrideAddress(data.Sender)

ifsenderStrideAddress==""{

returnerrorsmod.Wrapf(sdkerrors.ErrInvalidAddress,fmt.Sprintf("invalidsenderaddress(%s)",data.Sender))

}

newStrideAddress:=packetMetadata.StrideAddress

//actualiza el airdrop

airdropId:=packetMetadata.AirdropId

k.Logger(ctx).Info(fmt.Sprintf("actualizando la dirección del lanzamiento aéreo%s(orig%s)a%sparael lanzamiento aéreo%s",

senderStrideAddress,data.Sender,newStrideAddress,airdropId))

returnk.claimKeeper.UpdateAirdropAddress(ctx,senderStrideAddress,newStrideAddress,airdropId)

}

La función convierte la dirección del paquete IBC del remitente en una dirección Stride denominada senderStrideAddress y extrae el airdropId y la nueva dirección airdrop newStrideAddress de los metadatos del paquete. Luego llama a UpdateAirdropAddress para actualizar un ClaimRecord abierto que coincida con la combinación de senderStrideAddress y airdropId con la nueva dirección.

Con la actualización de ClaimRecord, newStrideAddress ahora puede reclamar el airdrop. Es importante tener en cuenta que este mecanismo de actualización solo está protegido por la dirección del remitente especificada en el paquete IBC. Stride no realiza ninguna otra verificación para asegurarse de que los destinatarios reales activaron las actualizaciones de los airdrops.

Para comprender por qué esta es una vulnerabilidad grave, debemos analizar más de cerca IBC, el protocolo de comunicación entre cadenas de bloques.

Seguridad IBC

IBC es un mecanismo ligero de comunicación entre cadenas basado en clientes. De forma similar a los protocolos de red clásicos, el módulo central IBC abstrae muchos detalles de bajo nivel, lo que permite a los desarrolladores construir fácilmente sus propias integraciones sobre él. La conexión de una cadena habilitada para IBC (Cadena A) a otra cadena habilitada para IBC (Cadena B) se parece un poco a esto:

ClientesolomáquinacreadoencadenahabilitadaIBC[ClientID=06-solomáquina-6]

Creadotendermintclientonsoloequipo[ClientID=07-tendermint-M48f]

ConexióninicializadaencadenahabilitadaIBC[ConnectionID=conexión-4]

Conexión inicializada en una sola máquina [ID de conexión = conexión-Kinb]

Conexión confirmada en la cadena IBC habilitada [ID de conexión = conexión-4]

Conexión confirmada en una sola máquina [ID de conexión = conexión-Kinb]

CanalinicializadoencadenahabilitadaIBC[ChannelID=channel-0]

Canalinicializadoensolomáquina[ChannelID=channel-wwl6]

Canal confirmado en la cadena habilitada de IBC [ChannelID=channel-0]

Canal confirmado en una sola máquina [ChannelID=channel-wwl6]

¡Conexión establecida!

En el primer paso, se crea un cliente ligero IBC de la cadena A en la cadena B y viceversa. Un cliente IBC se identifica de forma única por su ID de cliente, que se utiliza para rastrear y verificar el estado de la cadena remota. Una vez que se crean los clientes, pueden conectarse a través de una conexión, que se inicia con un protocolo de enlace de cuatro vías. Esto crea un ConnectionEnd en la cadena A con clientes ligeros de la cadena B en A, y otro en la cadena B con clientes ligeros de la cadena A en B. Las conexiones, una vez creadas, son persistentes y cifradas por dos clientes ligeros.

La comunicación a través de conexiones también se divide en diferentes canales. Los canales se identifican por la conexión subyacente y los puertos de origen y destino. Cada puerto identifica un módulo en la cadena correspondiente conectada a través del IBC. El ChannelEnd asociado con la conexión se crea en ambas cadenas y se identifica por channel-id. Ahora se pueden transferir datos entre las dos cadenas a través del canal establecido.

Es importante recordar que IBC es un protocolo sin permiso por defecto. Esto significa que cualquiera puede conectar dos cadenas habilitadas para IBC sin autorización o aprobación previa. De hecho, IBC admite las llamadas Solo Machines [7] Estándar, un cliente no representa una cadena de bloques, sino un único host o máquina. Dado que el contenido del paquete IBC está completamente controlado por el remitente (generalmente el módulo de origen en la cadena de origen), los módulos que realizan operaciones privilegiadas en los paquetes IBC entrantes siempre deben verificar que el mensaje proviene de un canal confiable.

Vulnerabilidades

Sin embargo, en lo que respecta a Stride, falta la verificación de canales en el módulo x/piloto automático. El código asume que los paquetes ICS-20 IBC con una dirección de remitente en particular solo pueden ser enviados por alguien que tenga control sobre esa dirección. Esto es cierto si solo consideramos los módulos de transporte en cadenas de socios confiables como EVMOS, pero los atacantes pueden simplemente enviar datos de paquetes IBC totalmente controlados para usar clientes IBC maliciosos bajo su control. La explotación de esta vulnerabilidad es relativamente simple:

  1. Cree un cliente IBC malicioso
  2. Usando el cliente malicioso Craft para crear un canal IBC al módulo de transporte Stride IBC,
  3. Y envíe una transferencia IBC maliciosa usando la dirección de los ClaimRecords no reclamados como el campo De. Utilice el campo de memo de ClaimMetadata para activar el piloto automático y actualizar la dirección de airdrop a una cuenta de Stride controlada por el atacante.
  4. Roba el airdrop enviando MsgClaimFreeAmount al módulo x/claim

Corrección de errores

Al recibir nuestro informe inmediato, el contribuyente de Stride retiró rápidamente todos los fondos de la billetera del revendedor Airdrop para asegurarse de que no hubiera fondos en riesgo. Se implementó una solución a largo plazo para garantizar que los paquetes de actualización de direcciones de IBC airdrop lleguen a través del canal IBC de confianza correcto.

en conclusión

El fuerte soporte para la comunicación entre cadenas a través de IBC es una ventaja única del ecosistema Cosmos. Si bien IBC se basa en primitivas criptográficas sólidas, la integración segura requiere una buena comprensión del modelo de confianza subyacente. Los desarrolladores que se basan en IBC y los ingenieros de seguridad que revisan las integraciones de IBC deben revisar cuidadosamente la superficie de ataque expuesta a clientes o canales IBC maliciosos. Nos gustaría agradecer a los colaboradores de Stride por su manejo profesional y su rápida respuesta a este problema.

Enlace externo de WeChat

[1] Dirección del enlace original:

[2] x/reclamo:

[3] Cargar datos de asignación:

[4] Registro de reclamo:

[5] MsgClaimFreeAmount:

[6] x/piloto automático:

[7] Máquinas en solitario:

Ver originales
El contenido es solo de referencia, no una solicitud u oferta. No se proporciona asesoramiento fiscal, legal ni de inversión. Consulte el Descargo de responsabilidad para obtener más información sobre los riesgos.
  • Recompensa
  • Comentar
  • Compartir
Comentar
0/400
Sin comentarios
  • Anclado
Opere con criptomonedas en cualquier momento y lugar
qrCode
Escanee para descargar la aplicación Gate
Comunidad
Español
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)