🛡️

Cosmos Vulnerability Scanner — Informe de Auditoría

NovaCosmos Chain · Auditoría de seguridad pre-mainnet · Generado por CULTIVA IA

Proyecto NovaCosmos Chain v0.1.0
Cosmos SDK v0.50.3
IBC-Go v8.3.0
CosmWasm v1.5.0
Fecha 2026-06-16
Estado 🔴 CRÍTICO — No lanzar
📋

Resumen Ejecutivo

7.0
/ 10

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.

4
Crítico
6
Alto
8
Medio
5
Bajo
Pérdida de fondos (4 hallazgos)
Overflow aritmético en x/dex, inflación de tokens IBC, reentrancy en vault_strategy.wasm, signer mismatch en x/vault.
Parada de cadena (6 hallazgos)
No-determinismo en iteración de mapa, panic ABCI en EndBlock, reentrancy IBC, bypass AnteHandler, gap consensus.
🔴

Hallazgos Críticos — Pérdida de Fondos

4 críticos
CRITICAL-§18-001 CRÍTICO x/dex §18 Arithmetic Overflow
Arithmetic Overflow en Cálculo de Precio AMM
📁 x/dex/keeper/amm.go:247 🎯 Pérdida de fondos 🔗 consensus-critical: MsgSwap handler
Descripción

La 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.

Código Vulnerable
x/dex/keeper/amm.go:247 // CalculateSwapAmount returns token out for given token in func CalculateSwapAmount(balanceIn, balanceOut, amountIn sdk.Int) sdk.Int { // VULN: balanceIn * amountIn puede desbordar int256 numerator := balanceOut.Mul(amountIn) // overflow posible denominator := balanceIn.Add(amountIn) // resultado incorrecto return numerator.Quo(denominator) }
Escenario de Ataque
  1. El atacante deposita liquidez creando un pool con balanceOut cercano a 2^127 tokens.
  2. Envía un MsgSwap con amountIn = 1 satoshi para triggear la multiplicación desbordada.
  3. numerator desborda a valor negativo → Quo devuelve un valor enorme.
  4. El atacante recibe balanceOut completo del pool pagando 1 satoshi. Pérdida total para LPs.
Recomendación
Usar 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.
CRITICAL-IBC-§7-002 CRÍTICO IBC §7 Token Inflation
Inflación de Tokens IBC — Bypass de Validación de Denom
📁 x/dex/keeper/ibc_hooks.go:89 🎯 Pérdida de fondos / Mint no autorizado 🔗 consensus-critical: OnRecvPacket
Descripción

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.

Código Vulnerable
x/dex/keeper/ibc_hooks.go:89 func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) { var data transfertypes.FungibleTokenPacketData transfertypes.ModuleCdc.MustUnmarshalJSON(packet.GetData(), &data) // VULN: No valida denom trace path antes de AddLiquidity ibcDenom := ibctransferkeeper.DenomPathFromHash(data.Denom) k.AddLiquidityFromIBC(ctx, ibcDenom, data.Amount) // amount sin validar }
Escenario de Ataque
  1. Atacante abre canal IBC desde una cadena controlada hacia NovaCosmos.
  2. Envía paquete con denom falsificado: transfer/channel-0/transfer/channel-0/uNOVA con amount = 1000000.
  3. OnRecvPacket no verifica la ruta completa, acepta el paquete y acredita 1.000.000 uNOVA al pool.
  4. Atacante retira liquidez del pool, extrayendo NOVA legítimo sin haberlo depositado.
Recomendación
Usar 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.
CRITICAL-WASM-§1-003 CRÍTICO CosmWasm §1 Execute Reentrancy
Reentrancy en vault_strategy.wasm — Doble Withdraw
📁 contracts/vault_strategy/src/contract.rs:312 🎯 Pérdida de fondos 🔗 consensus-critical: execute::withdraw
Descripción

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.

Código Vulnerable
contracts/vault_strategy/src/contract.rs:312 pub fn execute_withdraw(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> Result<Response, ContractError> { let balance = BALANCES.load(deps.storage, &info.sender)?; ensure!(balance >= amount, ContractError::InsufficientFunds {}); // VULN: Transferencia ANTES de actualizar el storage let msg = BankMsg::Send { to_address: info.sender.to_string(), amount: coins(amount.u128(), "unova") }; // Actualización del balance ocurre DESPUÉS — reentrancy window BALANCES.save(deps.storage, &info.sender, &(balance - amount))?; // tarde Ok(Response::new().add_message(msg)) }
Escenario de Ataque
  1. Atacante despliega un contrato receptor malicioso que tiene un receive handler.
  2. Deposita 1000 NOVA en el vault con el contrato receptor como dirección.
  3. Llama a withdraw(1000). BankMsg::Send se ejecuta antes de actualizar BALANCES.
  4. El contrato receptor, al recibir fondos, llama a withdraw(1000) nuevamente — balance aún no actualizado.
  5. El ciclo se repite hasta vaciar el vault. Pérdida total de todos los depósitos.
Recomendación
Aplicar el patrón checks-effects-interactions estrictamente: actualizar 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.
CRITICAL-§1-004 CRÍTICO x/liquidstaking §1 Non-Determinism
Iteración No-Determinista de Mapa en BeginBlock
📁 x/liquidstaking/abci.go:45 🎯 Parada de cadena / Divergencia de estado 🔗 consensus-critical: BeginBlocker
Descripción

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.

Código Vulnerable
x/liquidstaking/abci.go:45 func BeginBlocker(ctx sdk.Context, k Keeper) { validators := k.GetActiveValidators(ctx) // retorna map[string]ValidatorRecord for addr, val := range validators { // VULN: map iteration no-determinista rewards := k.CalculateRewards(ctx, val) k.DistributeRewards(ctx, addr, rewards) // modifica state } }
Escenario de Ataque
  1. La cadena avanza al bloque N con 50+ validadores activos.
  2. Cada validador itera el mapa en orden diferente → diferentes DistributeRewards → diferentes app hashes.
  3. En el siguiente bloque, los validadores no alcanzan consenso sobre el app hash.
  4. La cadena se detiene permanentemente. Requiere hard fork coordinado para recuperarse.
Recomendación
Cambiar 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 altos
HIGH-§3-005 ALTO x/vault §3 ABCI Panic
Panic No Manejado en EndBlocker del Vault
📁 x/vault/abci.go:112 🎯 Parada de cadena 🔗 consensus-critical: EndBlocker
Descripción

La 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.

x/vault/abci.go:112 strategy := k.GetStrategy(ctx, vault.StrategyID) // panic si no existe strategy.Execute(ctx, vault.Balance)
Recomendación
Cambiar a 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.
HIGH-§4-006 ALTO x/vault §4 Signer Mismatch
Signer Mismatch en MsgUpdateVaultParams
📁 x/vault/msg_server.go:78 🎯 Escalación de privilegios 🔗 consensus-critical: msg_server
Descripción

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.

x/vault/msg_server.go:78 if msg.Authority != k.authority { // VULN: comparación string sin normalizar return nil, sdkerrors.ErrUnauthorized }
Recomendación
Usar 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.
ℹ️ Hallazgos altos adicionales (IBC-§9-007, §2-008, IBC-§14-009, §6-010): Incluyen reentrancy en procesamiento de acknowledgements IBC, bypass de AnteHandler por mensaje anidado en authz, gap en validación de consensus state para light clients, y fuga de eventos entre contextos CacheContext. Los 4 hallazgos restantes están documentados en archivos individuales en .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