Resumen Ejecutivo
RIESGO CRÍTICO — Lanzamiento bloqueado
Se detectaron 4 vulnerabilidades críticas que pueden resultar en pérdida total de fondos o parada permanente de cadena. El lanzamiento a mainnet está bloqueado hasta resolución completa de los hallazgos críticos y altos.
Hallazgos Críticos — Pérdida de Fondos
4 críticosLa función CalculateSwapAmount realiza multiplicaciones de sdk.Int sin verificar desbordamiento antes de dividir. Con balances de pool grandes (>2^127), la multiplicación intermedia desborda silenciosamente, devolviendo un precio incorrecto que permite extraer tokens a precio cero. El flujo es alcanzable directamente desde el msg_server.Swap.
- El atacante deposita liquidez creando un pool con balanceOut cercano a 2^127 tokens.
- Envía un MsgSwap con amountIn = 1 satoshi para triggear la multiplicación desbordada.
- numerator desborda a valor negativo → Quo devuelve un valor enorme.
- El atacante recibe balanceOut completo del pool pagando 1 satoshi. Pérdida total para LPs.
sdk.NewIntFromBigInt con aritmética de big.Int nativa, o verificar desbordamiento antes de operar. Aplicar el patrón Int.BigInt().Mul() con verificación de rango. Considerar migrar a math/big con tests de fuzzing sobre valores límite (2^127-1, 2^128-1). Ver: Cosmos SDK integer overflow advisory.
El hook OnRecvPacket en el módulo DEX no valida correctamente el denom del token recibido antes de hacer acreditar fondos en el pool. Un paquete IBC malformado con un denom que contiene trazas de path múltiples puede bypasear la verificación de equivalencia y hacer que el contabilizador del pool asigne más tokens de los realmente transferidos.
- Atacante abre canal IBC desde una cadena controlada hacia NovaCosmos.
- Envía paquete con denom falsificado:
transfer/channel-0/transfer/channel-0/uNOVAcon amount = 1000000. - OnRecvPacket no verifica la ruta completa, acepta el paquete y acredita 1.000.000 uNOVA al pool.
- Atacante retira liquidez del pool, extrayendo NOVA legítimo sin haberlo depositado.
transfertypes.DenomTrace.IBCDenom() con validación completa de la traza antes de acreditar. Implementar whitelist de canales IBC de confianza. Verificar que el denom resultante esté registrado en el store de traces de IBC. Nunca llamar a AddLiquidity directamente desde hooks IBC sin validación completa.
La función execute_withdraw en vault_strategy.wasm actualiza el balance del usuario después de enviar los fondos via BankMsg::Send. Un contrato receptor malicioso puede re-ejecutar withdraw en el callback, drenando el vault antes de que el balance se actualice en el store. El patrón checks-effects-interactions no se aplica.
- Atacante despliega un contrato receptor malicioso que tiene un
receivehandler. - Deposita 1000 NOVA en el vault con el contrato receptor como dirección.
- Llama a withdraw(1000). BankMsg::Send se ejecuta antes de actualizar BALANCES.
- El contrato receptor, al recibir fondos, llama a withdraw(1000) nuevamente — balance aún no actualizado.
- El ciclo se repite hasta vaciar el vault. Pérdida total de todos los depósitos.
BALANCES antes de construir cualquier mensaje de salida. En CosmWasm, el orden de actualización de storage es: (1) validar, (2) modificar state, (3) construir mensajes de respuesta. Añadir tests de reentrancy con contratos maliciosos en el suite.
El BeginBlocker de x/liquidstaking itera sobre un mapa Go (map[string]ValidatorRecord) para calcular distribución de recompensas. En Go, la iteración sobre mapas es no-determinista por diseño. Diferentes validadores producirán distintos órdenes de iteración, resultando en state roots divergentes y una parada de cadena inmediata.
- La cadena avanza al bloque N con 50+ validadores activos.
- Cada validador itera el mapa en orden diferente → diferentes DistributeRewards → diferentes app hashes.
- En el siguiente bloque, los validadores no alcanzan consenso sobre el app hash.
- La cadena se detiene permanentemente. Requiere hard fork coordinado para recuperarse.
GetActiveValidators para devolver []ValidatorRecord ordenado determinísticamente por dirección (sort.Slice sobre las claves). O usar iteración sobre el KVStore (que garantiza orden lexicográfico). Nunca iterar sobre mapas Go en rutas BeginBlock/EndBlock/FinalizeBlock.
Hallazgos Altos — Parada de Cadena / Alto Riesgo
6 altosLa función rebalanceVaults ejecutada en EndBlocker llama a k.GetStrategy(ctx, strategyID) sin comprobar si la estrategia existe. Si un vault referencia una estrategia eliminada (posible mediante gobernanza), se produce un panic no capturado que detiene todos los nodos simultáneamente.
GetStrategy que retorne (Strategy, bool) y verificar la existencia antes de usar. Envolver el EndBlocker con defer/recover para capturar panics y emitir un evento de error en lugar de detener el nodo. Añadir test de regresión para estrategias eliminadas.
El mensaje MsgUpdateVaultParams verifica que el signer sea el Authority del módulo (governance), pero la verificación compara un string sin normalizar contra la dirección bech32. Una dirección con diferente casing podría bypassear la verificación en algunas condiciones de SDK v0.50.
sdk.MustAccAddressFromBech32(msg.Authority).Equals(sdk.MustAccAddressFromBech32(k.authority)) para comparar direcciones normalizadas. O aplicar el patrón estándar de SDK v0.50: if msg.Authority != k.GetAuthority() { return sdkerrors.ErrUnauthorized } con GetAuthority retornando la dirección canonizada.
.bughunt_cosmos/.
Cobertura de Patrones — 54/54 Verificados
| Agente | Referencia | Patrones | Hallazgos | Cobertura |
|---|---|---|---|---|
core-scanner |
VULNERABILITY_PATTERNS.md §1-9 | 8 | 2C 3M | 8/8 |
state-scanner |
STATE_VULNERABILITY_PATTERNS.md §11-23 | 13 | 1H 3M 2L | 13/13 |
advanced-scanner |
ADVANCED_VULNERABILITY_PATTERNS.md §24-27 | 4 | 2M 1L | 4/4 |
ibc-scanner |
IBC_VULNERABILITY_PATTERNS.md §1-16 | 16 | 1C 2H 1L | 16/16 |
evm-scanner |
EVM_VULNERABILITY_PATTERNS.md §1-10 | 10 | 0 | 10/10 |
cosmwasm-scanner |
COSMWASM_VULNERABILITY_PATTERNS.md §1-3 | 3 | 1C 1H | 3/3 |
Archivos de Hallazgos — .bughunt_cosmos/
| Archivo | Severidad | Módulo | Patrón |
|---|---|---|---|
critical-s18-arithmetic-overflow-amm.md |
CRÍTICO | x/dex | §18 |
critical-ibc-s7-token-inflation-bypass.md |
CRÍTICO | IBC | IBC §7 |
critical-wasm-s1-reentrancy-vault-strategy.md |
CRÍTICO | CosmWasm | WASM §1 |
critical-s1-map-nondeterminism-liquidstaking.md |
CRÍTICO | x/liquidstaking | §1 |
high-s3-abci-panic-endblock-vault.md |
ALTO | x/vault | §3 |
high-s4-signer-mismatch-vault-params.md |
ALTO | x/vault | §4 |
high-ibc-s9-reentrancy-ack-packet.md |
ALTO | IBC | IBC §9 |
high-s2-antehandler-bypass-authz.md |
ALTO | x/dex | §2 |
high-ibc-s14-consensus-state-gap.md |
ALTO | IBC | IBC §14 |
high-s6-cachecontext-event-leak.md |
ALTO | x/vault | §6 |
medium-s11-bookkeeping-drift-dex.md |
MEDIO | x/dex | §11 |
medium-s15-unbounded-pagination-vault.md |
MEDIO | x/vault | §15 |
... (6 archivos medios adicionales) |
MEDIO | varios | — |
... (5 archivos bajos) |
BAJO | varios | — |
Plan de Remediación
| Prioridad | Acción | Deadline | Responsable |
|---|---|---|---|
| P0 | Fijar overflow aritmético en x/dex/keeper/amm.go | 24h | Equipo Core |
| P0 | Aplicar CEI pattern en vault_strategy.wasm | 24h | Equipo Wasm |
| P0 | Ordenar iteración en x/liquidstaking BeginBlock | 48h | Equipo Core |
| P0 | Validar denom trace completo en IBC hooks | 48h | Equipo IBC |
| P1 | Resolver 6 hallazgos altos restantes | 1 semana | Todos |
| P2 | Resolver 8 hallazgos medios | 2 semanas | Todos |
| RE-AUDIT | Re-auditoría de hallazgos críticos y altos remediados | Semana 3 | CULTIVA IA |