Generated banner for Cosmos Vulnerability Scanner
Generated logo for Cosmos Vulnerability Scanner

Cosmos Vulnerability Scanner

Trail of BitsOfficialPublished by Trail of Bits

  • Skill
  • Workflow
  • Security
  • CC-BY-SA-4.0
  • No issues found

From the author

Scans Cosmos SDK blockchain modules and CosmWasm contracts for consensus-critical vulnerabilities — chain halts, fund loss, state divergence. 25 core + 16 IBC + 10 EVM + 3 CosmWasm patterns. Use when auditing custom x/ modules, reviewing IBC integrations, or assessing pre-launch chain security. Updated for SDK v0.53.x.
— the description by Trail of Bits

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The Cosmos Vulnerability Scanner is an automated security auditing tool designed for Cosmos SDK-based blockchains. It performs a multi-phase analysis of custom modules and smart contracts to identify vulnerabilities that could lead to chain halts, state divergence, or loss of funds. The skill orchestrates parallel scanning agents that evaluate code against specific patterns related to core SDK logic, IBC integrations, EVM compatibility, and CosmWasm contracts. It emphasizes consensus-critical execution paths, ensuring that findings are relevant to the blockchain's stability and security.

What it does

  • Generates a technical inventory and threat model via a discovery phase.
  • Spawns parallel scanning agents to analyze code against predefined vulnerability patterns.
  • Filters findings to focus exclusively on consensus-critical execution paths.
  • Supports specialized scanning for IBC, EVM, and CosmWasm components.
  • Writes detailed vulnerability reports as individual markdown files.
  • Verifies that all required security patterns have been assessed.

When to use it

  • Auditing custom Cosmos SDK modules.
  • Reviewing CosmWasm smart contracts for security flaws.
  • Conducting pre-launch security assessments for Cosmos-based chains.
  • Investigating potential causes of chain halt incidents.

Advantages

  • Reduces manual audit time through parallelized scanning.
  • Focuses on high-impact consensus-critical bugs.
  • Provides structured, actionable findings for remediation.
  • Adapts to specific Cosmos SDK version requirements.

Usage examples

“Run a security scan on the current directory.”
The agent performs a discovery phase, identifies the platform and SDK version, executes parallel scans, and generates a report in the .bughunt_cosmos/ directory.
“Audit my custom x/ modules and save findings to /tmp/audit_results.”
The agent scans the codebase for consensus-critical vulnerabilities and outputs individual markdown files containing findings to the specified directory.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/trailofbits/cosmos-vulnerability-scanner/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip cosmos-vulnerability-scanner.zip -d .agents/skills

Documentation by Trail of Bits

Cosmos Vulnerability Scanner

Purpose

Scan Cosmos SDK modules and CosmWasm contracts for vulnerabilities that cause chain halts, consensus failures, or fund loss. Spawns parallel scanning agents — each specializing in a vulnerability category — that return findings to the main skill, which then writes them as individual markdown files to an output directory.

Output directory: defaults to .bughunt_cosmos/. If the user specifies a different directory in their prompt, use that instead.

When to Use

  • Auditing Cosmos SDK modules (custom x/ modules)

  • Reviewing CosmWasm smart contracts

  • Pre-launch security assessment of Cosmos chains

  • Investigating chain halt incidents

When NOT to Use

  • Pure Solidity/EVM audits without Cosmos SDK — use Solidity-specific tools

  • CometBFT consensus engine internals — this covers SDK modules, not the consensus layer itself

  • General Go code review with no blockchain context

  • Cosmos SDK application logic that is not consensus-critical (e.g., CLI commands, REST endpoints)

  • CosmWasm contract-only audits on chains without custom SDK modules — use the CosmWasm checklist items alone

Essential Principles

  1. Consensus path is king — A bug only matters for chain halt/fund loss if it's on the consensus-critical execution path (BeginBlock, EndBlock, FinalizeBlock, msg_server handlers, AnteHandler). Always verify a finding is reachable from consensus before reporting it.

  2. State divergence = chain halt — Any non-determinism that causes validators to compute different state roots will halt the chain. This is the highest-severity class because it affects all validators simultaneously.

  3. Check the version — Cosmos SDK has breaking changes across major versions (v0.47 removed GetSigners, v0.50 added ABCI 2.0, v0.53 deprecated ValidateBasic). Always check go.mod versions before applying patterns.

  4. False positives waste audit time — A map iteration in a CLI command is not a consensus bug. A panic in a query handler does not halt the chain. Verify the execution context before flagging.

  5. Cross-module interactions are where bugs hide — The most severe findings (IBC reentrancy, EVM/Cosmos state desync, authz escalation) involve interactions between modules, not bugs within a single module.

Scanning Workflow

Phase 1: Discovery (synchronous)

Entry: Target codebase path provided by user. Codebase contains Go source (e.g., x/ modules, go.mod) or Rust contracts with cosmwasm_std.

Run a synchronous subagent (Agent tool) with the full contents of DISCOVERY.md as its prompt. The agent must:

  1. Follow the Discovery workflow to explore the target codebase

  2. Return the full CLAUDE.md content (the technical inventory and threat model) in its response

  3. Return a structured summary with exactly these fields:

PLATFORM: pure-cosmos | evm | wasm        (pick one; if multiple, comma-separated)
IBC_ENABLED: true | false
SDK_VERSION: <version from go.mod>
IBC_GO_VERSION: <version from go.mod, or "n/a">
CUSTOM_MODULES: <comma-separated list of x/* modules>

After the subagent returns, you (the main skill) Write the CLAUDE.md to the target repo root. Save its path and the discovery values — these feed into Phase 2.

Exit: CLAUDE.md written by main skill. PLATFORM, IBC_ENABLED, SDK_VERSION, IBC_GO_VERSION, and CUSTOM_MODULES captured.

Phase 2: Parallel Vulnerability Scan

Spawn scanning agents in a single message for maximum parallelism. Use the Agent Prompt Template below, filling in the reference file for each agent. Subagents only need read access (Grep, Glob, Read) — they return findings in their response and the main skill writes the files.

Always spawn these 3 agents:

Agent NameReference FileScope
core-scannerVULNERABILITY_PATTERNS.md§1-9: non-determinism, ABCI, signers, validation, handlers, ante security
state-scannerSTATE_VULNERABILITY_PATTERNS.md§11-23: bookkeeping, bank, pagination, events, tx replay, governance, arithmetic, encoding, deprecated modules
advanced-scannerADVANCED_VULNERABILITY_PATTERNS.md§24-27: storage keys, consensus validation, circuit breaker, crypto

Spawn conditionally (in the same parallel message):

Agent NameConditionReference File
evm-scannerPLATFORM includes evmEVM_VULNERABILITY_PATTERNS.md
ibc-scannerIBC_ENABLED is trueIBC_VULNERABILITY_PATTERNS.md
cosmwasm-scannerPLATFORM includes wasmCOSMWASM_VULNERABILITY_PATTERNS.md
Agent Prompt Template

Construct each agent's prompt by replacing {REFERENCE_FILE_PATH} with the full path to the reference file (under {baseDir}/resources/) and {CLAUDE_MD_PATH} with the path to the CLAUDE.md written in Phase 1:

Perform a very thorough security scan of a Cosmos SDK codebase for specific vulnerability patterns.

CONTEXT:
Read {CLAUDE_MD_PATH} for codebase context (SDK version, modules, threat model, key files).

PATTERNS:
Read {REFERENCE_FILE_PATH} — it contains numbered vulnerability patterns. For EACH pattern:
1. Read the detection patterns and "What to Check" items
2. Use Grep and Glob to search the target codebase for each pattern
3. When a match is found, Read surrounding code to verify it's on a consensus-critical path (BeginBlock, EndBlock, FinalizeBlock, msg_server handlers, AnteHandler)
4. Classify severity per the guidelines below

RULES:
- Consensus path only: Only flag code reachable from consensus-critical execution. CLI/query/test code is NOT a finding.
- Check SDK version in go.mod before applying patterns (v0.47 removed GetSigners, v0.50 added ABCI 2.0, v0.53 deprecated ValidateBasic).
- Always use the Grep tool for searches, not bash grep. The reference file contains search patterns — use them directly with the Grep tool.
- Ignore cross-references to other resource files (e.g., links to IBC or COSMWASM patterns). Those patterns are covered by other scanning agents.
- Reject these rationalizations:
  - "ValidateBasic catches this" — deprecated and facultative since SDK v0.53
  - "Behind governance, so safe" — governance proposals can be malicious
  - "IBC counterparty is trusted" — any chain can open a channel
  - "Panic can't happen, input is validated" — trace the full call chain
  - "Rounding error is only a few tokens" — compounds over time, can be looped
  - "EVM precompile handles rollback" — many have incomplete rollback

SEVERITY:
- Critical (fund loss): signer mismatch, broken bookkeeping, AnteHandler bypass, bank keeper misuse, IBC token inflation, EVM/Cosmos desync, Merkle proof forgery, arithmetic overflow
- High (chain halt): non-determinism, ABCI panics, slow ABCI, non-deterministic IBC acks, consensus gaps, CacheContext event leak
- Medium (DoS): unbounded pagination, tx replay, missing validation, governance spam, rate limiting, circuit breaker bypass, storage key collisions
- Low (logic): rounding errors, stub handlers, event override, module ordering

OUTPUT — RETURN FORMAT:
Do NOT write any files. Return ALL findings and the summary in your response.

For each pattern, return one of:
  §NUM PATTERN_NAME: Not applicable — [one-line reason]
  §NUM PATTERN_NAME: FINDING (followed by the finding block below)

For each finding, include the full content using this template:

FINDING_FILE: {SEVERITY}-s{SECTION_NUM}-{kebab-description}.md
## [SEVERITY] Title
**Location**: `file:line`
**Description**: What the bug is and why it matters
**Vulnerable Code**: [snippet]
**Attack Scenario**: [numbered steps]
**Recommendation**: How to fix
**References**: [links to relevant advisories or building-secure-contracts]

You MUST report on ALL patterns in the reference file — do not skip any.

Exit: All scanning agents returned. Each reported on every pattern in their reference file.

Phase 3: Write Findings

After all scanning agents return, write finding files to the output directory (default .bughunt_cosmos/):

  1. Parse each agent's response for FINDING_FILE: blocks

  2. For each finding, Write the content to {OUTPUT_DIR}/{filename} using the filename from FINDING_FILE:

  3. Create the output directory first if it doesn't exist

Phase 4: Verify Completeness

After writing all findings, verify every pattern was assessed:

  1. Collect the summary lines (§NUM entries) returned by each agent

  2. Check pattern counts against expected totals:

    • core-scanner: 8 patterns (§1-9, excluding §8 legacy-only)

    • state-scanner: 13 patterns (§11-23)

    • advanced-scanner: 4 patterns (§24-27)

    • evm-scanner (if spawned): 10 patterns (§1-10)

    • ibc-scanner (if spawned): 16 patterns (§1-16)

    • cosmwasm-scanner (if spawned): 3 patterns (§1-3)

  3. If any pattern is missing from a summary, flag it and re-prompt that agent

  4. List all finding files written to the output directory with a Glob for *.md

Exit: All patterns accounted for. Finding files listed for the user.


Success Criteria

  • [ ] Discovery CLAUDE.md written with complete technical inventory and threat model

  • [ ] All scanning agents completed and reported on every pattern in their reference file

  • [ ] Pattern counts verified against expected totals (no patterns skipped)

  • [ ] All findings written to output directory as individual markdown files

  • [ ] Each finding file includes: severity, location, vulnerable code, attack scenario, recommendation


Resources

Files

resources/IBC_VULNERABILITY_PATTERNS.md

IBC Vulnerability Patterns & Audit Checks

IBC-specific vulnerabilities for Cosmos chains integrating ibc-go. 16 vulnerability patterns covering middleware bugs, channel/denom handling, Interchain Accounts misuse, reentrancy, and general IBC integration checks. Referenced by SKILL.md.

1 UNTRUSTED IBC COUNTERPARTY CHAIN

Description: An attacker can create a malicious chain and spoof IBC message fields. The counterparty chain controls the sender, memo, and other packet data fields. Any IBC middleware or hook that acts on these fields must validate against a channel/counterparty whitelist. With ICS-20 v2 forwarding (ibc-go v9+), a forwarded packet may originate from a chain several hops away with no direct trust relationship.

What to Check:

  • [ ] Chains that can connect are authorized via a channel/counterparty whitelist

  • [ ] IBC middleware and hooks that act on sender, memo, or other packet data fields validate the source channel

  • [ ] With ICS-20 v2 forwarding, multi-hop trust implications are considered (forwarded packets may originate from untrusted chains)

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • OnRecvPacket|OnAcknowledgementPacket — then check results for sender or memo usage

References:


2 UNSAFE LookupModuleByPort AND LookupModuleByChannel USAGE

Description: [ibc-go < v10 only] LookupModuleByPort and LookupModuleByChannel were problematic APIs in the IBC capability module. They were removed in v10 along with the capability module (#7239, #7252), replaced by a static port router (PortKeeper.GetRoute()). On older ibc-go versions, incorrect usage of these APIs can lead to wrong module routing.

What to Check:

  • [ ] If module has access to ibc keeper, LookupModuleByPort and LookupModuleByChannel usages are carefully reviewed

  • [ ] On ibc-go v10+, verify these APIs are no longer used and the static port router is used instead

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • LookupModuleByPort|LookupModuleByChannel

References:


3 MISSING IBC CHANNEL CLOSE VALIDATION

Description: If a user should NOT be able to close an IBC channel, the OnChanCloseInit callback must return an error. Failing to do so allows any user to close the channel, disrupting the IBC application. The transfer module explicitly rejects channel close as a reference pattern.

Detection Patterns:

// VULNERABLE: Missing or permissive channel close callback
func (im IBCModule) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
    return nil  // Allows anyone to close the channel!
}

What to Check:

  • [ ] OnChanCloseInit returns an error if channels should not be closable by users

  • [ ] Channel close behavior is explicitly tested

Mitigation:

// SECURE: Reject channel close
func (im IBCModule) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
    return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "channel close not allowed")
}

4 INCORRECT IBC PACKET TIMEOUT HANDLING

Description: When an IBC packet times out or fails, the application must correctly refund tokens. On the source chain, escrowed tokens must be unescrowed; on the sink chain, burned vouchers must be minted back. See refundPacketToken in v8 (renamed to refundPacketTokens in v10 with InternalTransferRepresentation type). The core logic (source chain = unescrow, sink chain = mint back) is unchanged across versions.

What to Check:

  • [ ] Application handles packet timeout correctly — tokens are refunded on failed sends

  • [ ] Refund logic uses the same source/sink determination as the send path

  • [ ] OnTimeoutPacket is implemented and tested for all packet types

  • [ ] State changes from a failed send are fully rolled back

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • OnTimeoutPacket|refundPacketToken


5 INCORRECT IBC CHANNEL ORDERING TYPE

Description: ORDERED channels are closed on timeout — if a single packet times out, the entire channel is permanently closed. If message order doesn't matter, use UNORDERED to avoid this. Note: ORDERED_ALLOW_TIMEOUT was proposed but never merged into ibc-go.

What to Check:

  • [ ] Channel type (ORDERED vs UNORDERED) matches application invariants

  • [ ] If using ORDERED channels, the application handles channel closure on timeout gracefully

  • [ ] If message order doesn't matter, UNORDERED is used to avoid timeout-triggered channel closure


6 IBC TRANSFERS BREAKING INTERNAL BOOKKEEPING

Description: IBC transfers always mint/burn coins via x/bank. If a module maintains internal accounting (e.g., total deposited, collateral tracking), IBC transfers can silently break those invariants by moving tokens without going through the module's accounting path. Use blocklist, SendEnabled, or SendRestriction to control. In newer Cosmos SDK versions, SendRestriction is checked before deducting coins (SDK #20517), making it a more reliable defense. With ICS-20 v2 forwarding, tokens can arrive via unexpected multi-hop paths, amplifying bookkeeping risks.

What to Check:

  • [ ] IBC transfers cannot bypass internal bookkeeping for tokens managed by the module

  • [ ] Module uses blocklist, SendEnabled, or SendRestriction to prevent unauthorized IBC movement of managed tokens

  • [ ] With ICS-20 v2 forwarding, multi-hop token arrival paths are considered

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • SendRestriction|BlockedAddr|SendEnabled

References:


7 NON-DETERMINISTIC JSON IN IBC ACKNOWLEDGEMENTS

Description: Custom IBC middleware that constructs or deserializes acknowledgement bytes using Go's encoding/json can produce non-deterministic results across different validators. Go's JSON unmarshaller does not guarantee consistent output for map key ordering, numeric precision for large numbers, unknown field handling, and Unicode normalization. If any middleware in the chain's IBC application stack introduces a custom ack format or manipulates ack bytes using standard JSON, different validators may compute different state transitions, halting the chain. Any user that can open an IBC channel can introduce this state — this is trivially exploitable.

Known incidents: ISA-2025-001 (GHSA-4wf3-5qj9-368v), ASA-2025-004 (GHSA-jg6f-48ff-5xrw) Affected versions: ibc-go >= v7 (earlier versions may also be affected) Patched in: v7.10.0, v8.7.0

Detection Patterns:

// VULNERABLE: Using encoding/json for IBC acknowledgements
func (m MyMiddleware) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, ack []byte) error {
    var result map[string]interface{}
    json.Unmarshal(ack, &result)  // NON-DETERMINISTIC for maps!
    newAck, _ := json.Marshal(result)  // Different output across validators
    // ...
}

What to Check:

  • [ ] Custom IBC middleware does NOT produce acknowledgement bytes using json.Marshal on types containing maps or floating-point numbers

  • [ ] Acknowledgement serialization uses protobuf (not JSON), or a deterministic JSON marshaller (e.g., cosmossdk.io/x/tx/signing/aminojson)

  • [ ] All middleware in the IBC stack uses deterministic serialization

  • [ ] Tests do not assume determinism from running on a single machine

Mitigation:

// SECURE: Use protobuf for acknowledgement serialization
func (m MyMiddleware) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) ibcexported.Acknowledgement {
    // Use the channeltypes ack types which serialize via protobuf
    return channeltypes.NewResultAcknowledgement([]byte{byte(1)})
}

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • json\.(Marshal|Unmarshal) — then check results for ack or acknowledgement context

References:


8 PFM INVERTED ESCROW/MINT LOGIC

Description: In packet-forward-middleware, the moveFundsToUserRecoverableAccount() function (for nonrefundable forwarded packets) has the if/else branches for escrow vs. mint inverted relative to what SendTransfer() actually does. When a nonrefundable forward fails, the buggy code MINTS new tokens instead of UNESCROWING (or vice versa), inflating supply. An attacker sends tokens from Chain A to Chain B with a PFM forward to Chain C (marked nonrefundable). When the forward fails, PFM mints new tokens instead of unescrowing. The attacker sends the minted vouchers back to Chain A, draining A's escrow of real tokens. The attack is repeatable: each round inflates supply by the transfer amount.

Detection Patterns:

In relay.go SendTransfer():
  if HasPrefix(sourcePort, sourceChannel) -> BURN
  else (!HasPrefix)                       -> ESCROW

In WriteAcknowledgementForForwardedPacket() [CORRECT, non-nonrefundable path]:
  if !HasPrefix -> tokens were ESCROWED   -> code UNESCROWS from escrow  [correct]
  else HasPrefix -> tokens were BURNED     -> code MINTS back            [correct]

In moveFundsToUserRecoverableAccount() [BUGGY, nonrefundable path]:
  if !HasPrefix -> tokens were ESCROWED   -> code MINTS (WRONG!)
  else HasPrefix -> tokens were BURNED     -> code UNESCROWS (WRONG!)

What to Check:

  • [ ] Every code path that reverses a transfer uses the same source/sink logic (HasPrefix) as the original send

  • [ ] Nonrefundable forwarding paths have explicit tests comparing escrow/mint direction against the refundable path

  • [ ] PFM is fuzz-tested with various denom trace configurations

Mitigation:

// SECURE: Verify source/sink logic is consistent across all refund paths
// The escrow/mint decision in error-handling paths must mirror SendTransfer():
//   !HasPrefix (escrowed on send) -> UNESCROW on refund
//   HasPrefix  (burned on send)   -> MINT on refund

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • HasPrefix|SenderChainIsSource|ReceiverChainIsSource — source/sink logic

  • nonrefundable|Nonrefundable — nonrefundable forwarding


9 ICS-20 V2 MULTI-TOKEN PARTIAL RECEIVE

Description: ICS-20 v2 allows sending multiple tokens in a single packet. The onRecvPacket function processes them sequentially in a for loop. If token N fails (e.g., insufficient escrow for unescrow), the loop breaks and returns an error acknowledgement. However, tokens 1..N-1 have already been minted/unescrowed on the receiver chain and are NOT rolled back. The sender chain then receives the error ack and calls refundTokens, which refunds ALL tokens — including the ones that were successfully delivered to the receiver. Tokens end up existing on both chains simultaneously, created from nothing. The ICS-20 v2 spec pseudocode itself contains this bug — implementers who faithfully translate the spec inherit it.

Detection Patterns:

// VULNERABLE: Non-atomic multi-token processing
func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) error {
    var data types.FungibleTokenPacketDataV2
    // ...
    for _, token := range data.Tokens {
        // Each token modifies state immediately!
        if err := k.processToken(ctx, token); err != nil {
            return err  // Tokens 1..N-1 already committed, no rollback
        }
    }
    return nil
}

What to Check:

  • [ ] Multi-token onRecvPacket is wrapped in CacheContext to make all operations atomic

  • [ ] State is only committed if ALL tokens process successfully

  • [ ] Tests cover packets where the Nth token fails for all values of N (not just happy path or first-token-fails)

  • [ ] The ICS-20 v2 spec pseudocode itself contains this bug — verify the implementation does NOT faithfully copy it

Mitigation:

// SECURE: Wrap multi-token processing in CacheContext
func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) error {
    var data types.FungibleTokenPacketDataV2
    // ...
    cacheCtx, writeFn := ctx.CacheContext()
    for _, token := range data.Tokens {
        if err := k.processToken(cacheCtx, token); err != nil {
            return err  // Discard all cached writes
        }
    }
    writeFn()  // Commit only if ALL tokens succeeded
    return nil
}

10 UNPERMISSIONED IBC CHANNEL OPENING

Description: By default, anyone can open an IBC channel to any chain. If a chain's IBC application doesn't properly validate channel parameters (version, ordering, counterparty), a malicious actor can open channels with unexpected configurations. This was the precondition for the non-deterministic JSON ack vulnerability (§7) — "any user that can open an IBC channel can introduce this state."

Detection Patterns:

// VULNERABLE: No validation in channel open callbacks
func (im IBCModule) OnChanOpenInit(ctx sdk.Context, order channeltypes.Order,
    connectionHops []string, portID string, channelID string,
    chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty,
    version string) (string, error) {
    return version, nil  // Accepts ANY version, ordering, counterparty!
}

What to Check:

  • [ ] OnChanOpenInit / OnChanOpenTry implement strict version and ordering checks

  • [ ] Channel opening is permissioned (governance-gated) for sensitive applications

  • [ ] IBC application version strings are validated against an allowlist

  • [ ] Data from the counterparty chain is never trusted without validation

  • [ ] Unexpected port bindings are rejected

Mitigation:

// SECURE: Strict validation in channel open
func (im IBCModule) OnChanOpenInit(ctx sdk.Context, order channeltypes.Order,
    connectionHops []string, portID string, channelID string,
    chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty,
    version string) (string, error) {
    if order != channeltypes.UNORDERED {
        return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering,
            "expected %s, got %s", channeltypes.UNORDERED, order)
    }
    if version != types.Version {
        return "", errorsmod.Wrapf(types.ErrInvalidVersion,
            "expected %s, got %s", types.Version, version)
    }
    return version, nil
}

Grep Patterns (use Grep tool in x/):

  • OnChanOpenInit|OnChanOpenTry


11 IBC DENOM / VOUCHER CONFUSION

Description: IBC denomination traces encode the full path a token has traveled. The ibc/HASH format hides the actual trace, and developers often fail to validate that a received denom is legitimate. Key concepts that are frequently confused: Source zone (token native to sending chain — escrowed on sender, minted as voucher on receiver), Sink zone (token returning to origin — voucher burned on sender, unescrowed on receiver), Trace path (transfer/channel-0/transfer/channel-1/uatom — encodes every hop), Hashed denom (ibc/27394FB092D2ECCD56... — SHA256 hash of the trace path). In ICS-20 v2, MsgSendPacket allowed users to specify IBCDenom() (the hashed form) instead of requiring the full trace, letting users bypass trace validation (cosmos/ibc-go#7848). Before ibc-go v9, FungibleTokenPacketData JSON unmarshalling silently accepted unknown fields, allowing packet manipulation (cosmos/ibc-go#6215).

Known issue: cosmos/ibc-go#7848

What to Check:

  • [ ] Full denom trace is validated, not just the base denomination

  • [ ] Denom whitelists are implemented for sensitive operations (collateral, governance)

  • [ ] Security model of each IBC path is documented (which chains are trusted)

  • [ ] Unknown fields in packet data unmarshalling are rejected

  • [ ] DEXes/lending protocols do NOT treat ibc/HASH_A and ibc/HASH_B (both with base uatom) as equivalent

  • [ ] uatom arriving via channel-0 and uatom arriving via channel-1 are treated as DIFFERENT tokens (different security properties)

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • IBCDenom|DenomTrace|GetFullDenomPath — denom trace handling

  • ibc/ — then check results for denom, token, or transfer context


12 MISSING IBC RATE LIMITING

Description: Without rate limiting, a single exploit can drain an entire escrow in one block. Osmosis's rate limiting was explicitly credited with slowing down potential damage from the $126M reentrancy bug (ASA-2024-007).

What to Check:

  • [ ] Per-channel, per-denom, or aggregate rate limits are implemented on IBC transfers

  • [ ] Both inflows AND outflows are rate limited

  • [ ] Rate limits are set based on percentage of total supply, not absolute amounts

  • [ ] Channel opening itself is rate limited

  • [ ] Rate limit hits are monitored as an early warning system

  • [ ] Rate limits are governance-adjustable

Grep Patterns (use Grep tool in x/):

  • RateLimit|rate_limit|ratelimit

References:


13 ICA HOST ALLOW-ALL CONFIGURATION

Description: The Interchain Accounts (ICA) host module, when enabled, accepts a parameter allow_messages that controls which message types can be executed by remote controller chains. The default genesis configuration sets this to ["*"] — allowing ALL message types. If a chain enables ICA host with "*" and any other chain has a controller account on it, that controller chain can execute any message on the host chain, including governance proposals, staking operations, and module-specific messages. A chain may have ICA host enabled in its genesis JSON without the host keeper actually being wired, giving a false sense of security that disappears if the keeper is later connected (cosmos/ibc-go#6324).

Detection Patterns:

// VULNERABLE: Default genesis allows all messages
"host_genesis_state": {
    "params": {
        "host_enabled": true,
        "allow_messages": ["*"]
    }
}

What to Check:

  • [ ] allow_messages is set to a minimal whitelist — NEVER "*" in production

  • [ ] Controller chains that have accounts on the host are audited

  • [ ] ICA host enablement is governance-gated

  • [ ] A chain does not have ICA host enabled in genesis JSON without the host keeper actually being wired (false sense of security — cosmos/ibc-go#6324)

Mitigation:

// SECURE: Minimal whitelist
"host_genesis_state": {
    "params": {
        "host_enabled": true,
        "allow_messages": [
            "/cosmos.bank.v1beta1.MsgSend",
            "/cosmos.staking.v1beta1.MsgDelegate"
        ]
    }
}

Grep Patterns (use Grep tool in x/):

  • allow_messages|AllowMessages with glob: "*.{go,json}" — ICA allow list

  • RegisterInterchainAccount|SendTx — ICA usage

References:


14 ICA CHANNEL ORDERING BACKWARDS COMPATIBILITY

Description: In ibc-go v7.5.0+, the default channel ordering for MsgRegisterInterchainAccount changed from ORDER_ORDERED to ORDER_UNORDERED. Older ibc-go versions only support ORDER_ORDERED for ICA channels. If a chain upgrades to ibc-go >= 7.5.0 and tries to open an ICA channel to a chain running an older version, the channel open will fail silently or explicitly. Additionally, ICA channel upgrades that change the ordering (ordered to unordered or vice versa) are explicitly disallowed by a guard that had to be added after the fact (cosmos/ibc-go#5415), meaning chains that performed such upgrades before the guard could have broken ICA functionality.

What to Check:

  • [ ] Channel ordering is explicitly pinned when upgrading ibc-go versions

  • [ ] ICA functionality is tested against counterparty chains running older ibc-go

  • [ ] ICA channel states are monitored after upgrades

References:


15 ICA AUTHENTICATION MODULE MISSING OR MISCONFIGURED

Description: ICA provides two entry points (RegisterInterchainAccount and SendTx) that are NOT exposed to end users directly. They must be gated by a custom "Authentication module" that controls who can use ICA. If the authentication module is missing, misconfigured, or overly permissive, any user may be able to register interchain accounts or submit transactions through them. The ICA spec (ICS-27) leaves the authentication module's design entirely to the chain developer. The informal systems audit noted: "The requirements and the API between the authentication and the ICA modules are not clearly specified."

What to Check:

  • [ ] A custom authentication module gates ICA usage

  • [ ] RegisterInterchainAccount is restricted (governance, whitelisted addresses)

  • [ ] Each SendTx call validates the caller is authorized for the specific interchain account

  • [ ] ICA is NOT assumed to have built-in access control (it does not)


16 IBC REENTRANCY / CEI VIOLATIONS

Description: IBC's callback architecture (hooks, middleware) introduces external execution points between internal state operations. If a callback executes before internal state is finalized (e.g., packet commitment deleted), recursive re-entry is possible. This is a classic checks-effects-interactions (CEI) violation. The OnTimeoutPacket callback in ibc-go executed before the packet commitment was deleted — a malicious CosmWasm contract could re-enter via submessages since the commitment still existed. This class caused $6.4M in real losses and $150M+ at risk.

Known incidents: ASA-2024-007 ($150M+ at risk, patched April 2024), Terra IBC hooks ($6.4M stolen, July 2024 — chain had accidentally reintroduced the patched vulnerability during an upgrade)

Affected versions: ibc-go < v4.6.0, < v5.4.0, < v6.3.0, < v7.4.0, < v8.2.0

Detection Patterns:

// VULNERABLE: Callback before state finalization (CEI violation)
func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet) error {
    // 1. CHECK: Verify packet commitment exists
    commitment := k.GetPacketCommitment(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence)

    // 2. INTERACTION: Execute callback (ibc-hooks invokes CosmWasm contract)
    // Malicious contract returns another MsgTimeout as submessage
    // Commitment still exists → re-entry succeeds!
    k.callbacks.OnTimeoutPacket(ctx, packet)

    // 3. EFFECT: Delete commitment (too late — already re-entered)
    k.DeletePacketCommitment(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence)

    return nil
}
// SECURE: Effects before interactions (CEI pattern)
func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet) error {
    // 1. CHECK
    commitment := k.GetPacketCommitment(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence)

    // 2. EFFECT: Delete commitment BEFORE callback
    k.DeletePacketCommitment(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence)

    // 3. INTERACTION: Now callback cannot re-enter (commitment gone)
    k.callbacks.OnTimeoutPacket(ctx, packet)

    return nil
}

What to Check:

  • [ ] ibc-go version is patched (>= v4.6.0, v5.4.0, v6.3.0, v7.4.0, v8.2.0)

  • [ ] ALL IBC callbacks (OnRecvPacket, OnAcknowledgementPacket, OnTimeoutPacket) follow CEI: state mutation BEFORE external callbacks

  • [ ] Custom IBC middleware and hooks do NOT introduce new callback-before-finalization patterns

  • [ ] If the chain has been upgraded, verify the patch was NOT accidentally reverted (Terra re-introduced the vulnerability during a June 2024 upgrade)

  • [ ] CosmWasm-enabled chains with ibc-hooks middleware are at highest risk — CosmWasm submessages enable recursive re-entry

  • [ ] Rate limiting (§12) is implemented as defense-in-depth even with patched ibc-go

Vulnerable chain requirements: IBC-enabled + vulnerable ibc-go version + CosmWasm-enabled with permissionless uploads + ibc-hooks middleware wrapping ICS-20.

Two attack vectors:

  • Native token theft: Drain all tokens from ICS-20 escrow accounts

  • Infinite IBC token minting: Mint unbacked IBC tokens, bridge them back to original chains

Grep Patterns (use Grep tool with glob: "*.go" in x/):

  • OnTimeoutPacket|OnAcknowledgementPacket|OnRecvPacket — IBC callbacks

  • DeletePacketCommitment — verify commitment deletion happens BEFORE callback invocation

  • ibc-hooks|ibchooks — hooks middleware presence

References:


SUMMARY: MOST DANGEROUS PATTERNS
#PatternCategoryKnown Incident / Reference
1Untrusted IBC counterparty chainChannelGHSA-5jgq-x857-p8xw
2Unsafe LookupModuleByPort/LookupModuleByChannelChannelibc-go#71
3Missing IBC channel close validationChannel—
4Incorrect IBC packet timeout handlingChannel—
5Incorrect IBC channel ordering typeChannel—
6IBC transfers breaking internal bookkeepingDenomSDK#20517
7Non-deterministic JSON in IBC ack middlewareMiddlewareISA-2025-001, ASA-2025-004
8PFM inverted escrow/mint (nonrefundable)MiddlewarePoC available
9ICS-20 v2 multi-token partial receive inflationMiddlewarePoC available
10Unpermissioned IBC channel openingChannelPrecondition for ISA-2025-001
11IBC denom / voucher confusionDenomibc-go#7848
12Missing IBC rate limitingDefenseWould have limited $126M exploit
13ICA host allow-all ("*") configurationICADefault genesis config
14ICA channel ordering backwards compatICAibc-go#6714
15ICA authentication module missingICAICS-27 audit finding
16IBC reentrancy / CEI violationsReentrancyASA-2024-007, Terra $6.4M exploit

References

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Trail of Bits
Publisher
Trail of Bits (Official)
Source
trailofbits/skills · plugins/building-secure-contracts/skills/cosmos-vulnerability-scanner · commit e4dd0bb
Licence
Creative Commons Attribution-ShareAlike 4.0 International (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Trail of Bits or Trail of Bits.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download cosmos-vulnerability-scanner.zip