## 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](../SKILL.md).

### 1 UNTRUSTED IBC COUNTERPARTY CHAIN

**Description**: An attacker can create a malicious chain and [spoof IBC message fields](https://github.com/evmos/evmos/security/advisories/GHSA-5jgq-x857-p8xw). 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**:
- [GHSA-5jgq-x857-p8xw](https://github.com/evmos/evmos/security/advisories/GHSA-5jgq-x857-p8xw)

---

### 2 UNSAFE LookupModuleByPort AND LookupModuleByChannel USAGE

**Description**: **[ibc-go < v10 only]** [`LookupModuleByPort` and `LookupModuleByChannel`](https://github.com/cosmos/ibc-go/issues/71) were problematic APIs in the IBC capability module. They were removed in v10 along with the capability module ([#7239](https://github.com/cosmos/ibc-go/pull/7239), [#7252](https://github.com/cosmos/ibc-go/pull/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**:
- [ibc-go#71](https://github.com/cosmos/ibc-go/issues/71)
- [#7239](https://github.com/cosmos/ibc-go/pull/7239)
- [#7252](https://github.com/cosmos/ibc-go/pull/7252)

---

### 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](https://github.com/cosmos/ibc-go/blob/v8.5.2/modules/apps/transfer/ibc_module.go#L159-L166) as a reference pattern.

**Detection Patterns**:
```go
// 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**:
```go
// 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`](https://github.com/cosmos/ibc-go/blob/v8.5.2/modules/apps/transfer/keeper/relay.go) 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](https://github.com/cosmos/ibc-go/blob/v8.5.2/modules/core/04-channel/keeper/timeout.go) — 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](https://github.com/cosmos/cosmos-sdk/pull/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**:
- [SDK #20517](https://github.com/cosmos/cosmos-sdk/pull/20517)

---

### 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**:
```go
// 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**:
```go
// 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**:
- [ISA-2025-001](https://github.com/cosmos/ibc-go/security/advisories/GHSA-4wf3-5qj9-368v)
- [ASA-2025-004](https://github.com/cosmos/ibc-go/security/advisories/GHSA-jg6f-48ff-5xrw)

---

### 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**:
```go
// 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**:
```go
// 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**:
```go
// 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**:
```go
// 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**:
```go
// 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**:
- [Osmosis rate limits](https://www.range.org/blog/ibc-rate-limits-introduction-and-state-of-the-art-1-3)
- [Cosmos Hub proposal](https://forum.cosmos.network/t/proposal-890-passed-signaling-proposal-ibc-rate-limiting/13410)

---

### 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**:
```json
// 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**:
```json
// 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**:
- [Architecture review](https://github.com/cosmos/ibc-go/issues/631)
- [Genesis default issue](https://github.com/cosmos/ibc-go/issues/6324)

---

### 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**:
- [Issue](https://github.com/cosmos/ibc-go/issues/6714)
- [Fix](https://github.com/cosmos/ibc-go/pull/5415)

---

### 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**:
```go
// 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
}
```

```go
// 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**:
- [Asymmetric Research — IBC Reentrancy Infinite Mint](https://blog.asymmetric.re/cosmos-ibc-reentrancy-infinite-mint/)
- [GHSA-j496-crgh-34mx](https://github.com/cosmos/ibc-go/security/advisories/GHSA-j496-crgh-34mx)
- [Terra IBC hooks exploit — $6.4M](https://www.theblock.co/post/308440/attacker-exploits-ibc-hooks-vulnerability-to-steal-tokens-on-terra-blockchain)

---

### SUMMARY: MOST DANGEROUS PATTERNS

| # | Pattern | Category | Known Incident / Reference |
|---|---------|----------|---------------------------|
| 1 | Untrusted IBC counterparty chain | Channel | GHSA-5jgq-x857-p8xw |
| 2 | Unsafe LookupModuleByPort/LookupModuleByChannel | Channel | ibc-go#71 |
| 3 | Missing IBC channel close validation | Channel | — |
| 4 | Incorrect IBC packet timeout handling | Channel | — |
| 5 | Incorrect IBC channel ordering type | Channel | — |
| 6 | IBC transfers breaking internal bookkeeping | Denom | SDK#20517 |
| 7 | Non-deterministic JSON in IBC ack middleware | Middleware | ISA-2025-001, ASA-2025-004 |
| 8 | PFM inverted escrow/mint (nonrefundable) | Middleware | PoC available |
| 9 | ICS-20 v2 multi-token partial receive inflation | Middleware | PoC available |
| 10 | Unpermissioned IBC channel opening | Channel | Precondition for ISA-2025-001 |
| 11 | IBC denom / voucher confusion | Denom | ibc-go#7848 |
| 12 | Missing IBC rate limiting | Defense | Would have limited $126M exploit |
| 13 | ICA host allow-all (`"*"`) configuration | ICA | Default genesis config |
| 14 | ICA channel ordering backwards compat | ICA | ibc-go#6714 |
| 15 | ICA authentication module missing | ICA | ICS-27 audit finding |
| 16 | IBC reentrancy / CEI violations | Reentrancy | ASA-2024-007, Terra $6.4M exploit |

---

### References

- **IBC-Go Security Advisories**: https://github.com/cosmos/ibc-go/security/advisories
- **IBC-Go Audit Reports**: https://docs.cosmos.network/ibc/v8.5.x/security-audits
- **Asymmetric Research**: "Cosmos IBC Reentrancy Infinite Mint" — https://blog.asymmetric.re/cosmos-ibc-reentrancy-infinite-mint/
- **Jump Crypto**: "Huckleberry: IBC Event Hallucinations" — https://jumpcrypto.com/resources/huckleberry-ibc-event-hallucinations
- **Cosmos Forum**: IBC Security Advisories — https://forum.cosmos.network/c/security/
- **Osmosis Rate Limits**: https://www.range.org/blog/ibc-rate-limits-introduction-and-state-of-the-art-1-3
