Cosmos Vulnerability Scanner
Trail of BitsOfficialPublished by Trail of Bits
- Skill
- Workflow
- Security
- CC-BY-SA-4.0
- No issues found
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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/trailofbits/cosmos-vulnerability-scanner/downloadUnzip 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
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.
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.
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.modversions before applying patterns.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.
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:
Follow the Discovery workflow to explore the target codebase
Return the full CLAUDE.md content (the technical inventory and threat model) in its response
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 Name | Reference File | Scope |
|---|---|---|
core-scanner | VULNERABILITY_PATTERNS.md | §1-9: non-determinism, ABCI, signers, validation, handlers, ante security |
state-scanner | STATE_VULNERABILITY_PATTERNS.md | §11-23: bookkeeping, bank, pagination, events, tx replay, governance, arithmetic, encoding, deprecated modules |
advanced-scanner | ADVANCED_VULNERABILITY_PATTERNS.md | §24-27: storage keys, consensus validation, circuit breaker, crypto |
Spawn conditionally (in the same parallel message):
| Agent Name | Condition | Reference File |
|---|---|---|
evm-scanner | PLATFORM includes evm | EVM_VULNERABILITY_PATTERNS.md |
ibc-scanner | IBC_ENABLED is true | IBC_VULNERABILITY_PATTERNS.md |
cosmwasm-scanner | PLATFORM includes wasm | COSMWASM_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/):
Parse each agent's response for
FINDING_FILE:blocksFor each finding, Write the content to
{OUTPUT_DIR}/{filename}using the filename fromFINDING_FILE:Create the output directory first if it doesn't exist
Phase 4: Verify Completeness
After writing all findings, verify every pattern was assessed:
Collect the summary lines (§NUM entries) returned by each agent
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)
If any pattern is missing from a summary, flag it and re-prompt that agent
List all finding files written to the output directory with a
Globfor*.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
Discovery & CLAUDE.md: DISCOVERY.md
Core patterns (§1-9): VULNERABILITY_PATTERNS.md
State & module patterns (§11-23): STATE_VULNERABILITY_PATTERNS.md
Advanced patterns (§24-27): ADVANCED_VULNERABILITY_PATTERNS.md
IBC vulnerabilities: IBC_VULNERABILITY_PATTERNS.md
CosmWasm vulnerabilities: COSMWASM_VULNERABILITY_PATTERNS.md
EVM vulnerabilities: EVM_VULNERABILITY_PATTERNS.md
Building Secure Contracts:
building-secure-contracts/not-so-smart-contracts/cosmos/Cosmos SDK Docs: https://docs.cosmos.network/
CodeQL for Cosmos SDK: https://github.com/crypto-com/cosmos-sdk-codeql
Files
- ATTRIBUTION.mdAdded by AutoGineer975 B
- CHANGELOG.md12.6 KiB
- LICENSELicence file added by AutoGineer19.7 KiB
- SKILL.md10.7 KiB
- agents/openai.yaml256 B
- assets/trail-of-bits-mark.svg3.0 KiB
- resources/ADVANCED_VULNERABILITY_PATTERNS.md12.5 KiB
- resources/COSMWASM_VULNERABILITY_PATTERNS.md7.5 KiB
- resources/DISCOVERY.md2.9 KiB
- resources/EVM_VULNERABILITY_PATTERNS.md18.0 KiB
- resources/IBC_VULNERABILITY_PATTERNS.md27.5 KiB
- resources/STATE_VULNERABILITY_PATTERNS.md29.2 KiB
- resources/VULNERABILITY_PATTERNS.md26.4 KiB
resources/EVM_VULNERABILITY_PATTERNS.md
Cosmos EVM Vulnerability Patterns
Vulnerability patterns specific to Cosmos EVM chains (evmOS, Ethermint, Initia, Berachain, Sei, etc.). These chains run two execution runtimes (EVM + Cosmos SDK) with independent state that must stay in sync. This is the most frequently discovered vulnerability class in Cosmos EVM chains — 7+ findings across multiple chains, $7M+ in real losses. Referenced by SKILL.md.
1 EVM REVERT DOES NOT ROLLBACK COSMOS STATE
Description: Precompiles bridge EVM and Cosmos state machines. When a Solidity revert, try/catch, or out-of-gas (OOG) occurs, EVM state rolls back automatically, but Cosmos keeper writes made during the precompile call persist. An attacker wraps a precompile call in try/catch, triggers a revert after the Cosmos-side effect executes, and gets the EVM-side effect undone while keeping the Cosmos-side effect. Every Cosmos EVM chain is vulnerable until it implements snapshot-based atomic precompile wrappers.
Detection Patterns:
// VULNERABLE: Solidity try/catch around precompile call
// EVM state rolls back on revert, but Cosmos delegation persists
contract InfiniteDelegate {
IStaking constant STAKING = IStaking(0x...1003);
function exploit(address validator, uint256 amount) external {
try this._delegate(validator, amount) {} catch {}
// EVM: tokens refunded (revert undid the transfer)
// Cosmos: delegation still exists (keeper write persisted)
// Net effect: delegation increased without balance decreasing
}
function _delegate(address validator, uint256 amount) external {
STAKING.delegate(validator, amount);
revert(); // EVM state rolls back, Cosmos state does NOT
}
}What to Check:
[ ] ALL precompile
Run()methods wrapped in atomic snapshot/revert (no partial execution on OOG or error)[ ] EVM
revert/try/catch/ OOG propagates rollback to Cosmos keeper state — test with Soliditytry/catcharound every precompile call[ ] IBC operations triggered via precompiles are atomic with the EVM transaction
References:
EVM staking precompile infinite delegation (Finding 7)
Saga $7M exploit (Finding 5)
2 STATEDB.COMMIT() DURING PRECOMPILE EXECUTION
Description: Certain precompile calls (e.g., delegationTotalRewards) trigger StateDB.Commit(), writing state to permanent storage mid-execution. A subsequent revert only undoes cached in-memory state — permanent storage retains the committed state. This creates orphaned contracts with withdrawable balances. Exponential multiplication is possible: 1 ETH becomes 1,048,576 ETH in 20 loops.
Detection Patterns:
// VULNERABLE: Precompile triggers StateDB.Commit() (Finding 6)
// Calling certain precompiles caused StateDB.Commit() to write
// state to permanent storage. A subsequent revert only undoes
// cached in-memory state, but permanent storage retains the
// committed state.
// Result: Orphaned contracts with balances are accessible and withdrawable.What to Check:
[ ]
StateDB.Commit()is NEVER called during precompile execution (only at transaction finalization)[ ] Precompile code paths audited for any indirect
Commit()triggers (e.g., via keeper calls that flush caches)[ ] StateDB journal entries cover all state modifications made by precompiles
Grep Patterns (use Grep tool with glob: "*.go" in x/):
StateDB|stateDB— then check results forcommitcontextfunc.*Run\(.*vm\.EVM— precompile Run methods
References:
Evmos infinite mint (Finding 6)
3 PARTIAL PRECOMPILE EXECUTION (OOG)
Description: A precompile Run() performs multiple Cosmos state changes sequentially (e.g., transfer rewards then clear claimable counter). If execution runs out of gas between steps, early state changes persist while later ones are skipped. The attacker receives rewards but the claimable counter is never reset — repeat to drain the module.
Detection Patterns:
// VULNERABLE: Precompile Run() not atomic (Finding 20)
func (p DistributionPrecompile) Run(evm *vm.EVM, input []byte) ([]byte, error) {
// Step 1: Transfer rewards to user (succeeds)
k.bankKeeper.SendCoins(ctx, moduleAcc, userAddr, rewards)
// Step 2: Clear claimable rewards (runs out of gas here!)
k.SetClaimableRewards(ctx, userAddr, sdk.Coins{})
// User received rewards but claimable counter never reset
// Repeat to drain distribution module
}What to Check:
[ ] Every precompile
Run()is wrapped in an atomic execution wrapper (snapshot before, revert on any error including OOG)[ ] No multi-step state mutations without atomicity guarantees
[ ] Gas estimation for precompile operations is accurate (underestimation enables OOG at attacker-chosen points)
Mitigation:
// SECURE: Wrap in atomic execution
func (p DistributionPrecompile) Run(evm *vm.EVM, input []byte) ([]byte, error) {
return p.RunAtomic(evm, input, func(ctx sdk.Context) ([]byte, error) {
// All operations atomic — snapshot + revert on any error including OOG
})
}References:
Partial precompile state writes (Finding 20)
4 REVERTED EVM CALLS LEAVE COSMOS MESSAGES IN QUEUE
Description: When an EVM call dispatches a Cosmos message (e.g., IBC transfer) via a precompile, and the EVM call subsequently reverts, the dispatched Cosmos message remains in the execution queue. The Cosmos message executes despite the EVM revert, enabling IBC transfers (and other Cosmos-side effects) without corresponding EVM-side state changes.
Detection Patterns:
// VULNERABLE: Initia — dispatched Cosmos messages remain in execution queue
// even when the EVM call that dispatched them reverts (Finding 26b)
// Solidity try/catch wraps a call that dispatches IBC transfer via precompile
// Revert undoes EVM state but IBC transfer message still in Cosmos execution queue
// Result: IBC transfers execute despite explicit reversionWhat to Check:
[ ] Cosmos message dispatch queue is cleared when EVM execution reverts
[ ] All Cosmos messages dispatched from precompiles are tracked in the EVM journal and rolled back on revert
[ ] Test: wrap every message-dispatching precompile call in Solidity
try/catchwith explicitrevert— verify no Cosmos messages execute
References:
Initia Code4rena report (Finding 26b)
5 GAS LIMIT BYPASS VIA PRECOMPILE DISPATCH
Description: Solidity's call{gas: X}() restricts gas forwarded to an untrusted contract. But if that contract calls a precompile that dispatches a new Cosmos MsgCall, the new message gets the full parent transaction gas limit — not the restricted amount. This defeats gas-based reentrancy guards and any logic relying on gas limits to bound untrusted execution.
Detection Patterns:
// VULNERABLE: Initia — COSMOS_CONTRACT precompile bypasses gas restrictions (Finding 26c)
// Solidity call{gas: X}(untrustedContract) restricts gas
// But untrusted contract calls COSMOS_CONTRACT precompile to dispatch new MsgCall
// New MsgCall gets full parent transaction gas limit, not the restricted amountWhat to Check:
[ ] Gas forwarding through precompile dispatch does not bypass caller-imposed gas limits
[ ] Precompile-dispatched messages inherit the remaining gas of the calling frame, not the transaction-level gas limit
[ ] Gas-based reentrancy guards are not the sole defense (combine with mutex/state guards)
References:
Initia Code4rena report (Finding 26c)
6 WRONG ROUTING IN MULTI-DENOM ERC20 OPERATIONS
Description: When processing multiple denominations in a loop, ERC20-related operations (burn, mint, community pool funding) accidentally use the full amount parameter (all denoms) instead of the individual coin being processed. If the loop contains both ERC20 and native denoms, the native denom amount is sent to the wrong destination on every iteration that hits the ERC20 path.
Detection Patterns:
// VULNERABLE: Initia — BurnCoins sends entire amount to community pool (Finding 26a)
// When burning ERC20 denominated coins in a loop,
// the function sends the full `amount` parameter (all denoms)
// instead of just the individual `coin` being processed
func (k Keeper) BurnCoins(ctx sdk.Context, amount sdk.Coins) error {
for _, coin := range amount {
if k.isERC20(coin.Denom) {
// WRONG: sends ALL coins, not just this one
k.communityPoolKeeper.FundCommunityPool(ctx, amount, moduleAddr)
}
}
}What to Check:
[ ] Multi-denom operations in loops use the individual
coin, not the fullamountparameter[ ] ERC20/native denom branching logic tested with mixed-denom inputs
[ ] Community pool / burn / mint amounts match the specific coin being processed
Mitigation:
// SECURE: Use the individual coin in the loop
func (k Keeper) BurnCoins(ctx sdk.Context, amount sdk.Coins) error {
for _, coin := range amount {
if k.isERC20(coin.Denom) {
k.communityPoolKeeper.FundCommunityPool(ctx, sdk.NewCoins(coin), moduleAddr)
} else {
k.bankKeeper.BurnCoins(ctx, moduleName, sdk.NewCoins(coin))
}
}
}References:
Initia Code4rena report (Finding 26a)
7 ETHERMINT ANTE HANDLER BYPASS (NESTED MESSAGES)
Description: Cosmos EVM chains add custom AnteHandler decorators for EVM-specific validation (gas price floors, tx type restrictions, EVM-specific fee checks). These decorators typically only inspect the outer message. An attacker wraps a blocked EVM message inside x/authz MsgExec or an x/group proposal — the inner message skips EVM-specific ante validation entirely.
Detection Patterns:
// VULNERABLE: AnteHandler only checks outer message type
func (d EthGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
for _, msg := range tx.GetMsgs() {
// Only checks outer msg — does NOT recurse into MsgExec.Msgs!
if ethMsg, ok := msg.(*evmtypes.MsgEthereumTx); ok {
if err := d.validateGas(ethMsg); err != nil {
return ctx, err
}
}
}
return next(ctx, tx, simulate)
}What to Check:
[ ] Custom EVM AnteHandlers recurse into
x/authz MsgExecandx/groupproposal inner messages[ ] EVM-specific fee/gas validation applies to nested EVM messages, not just top-level
[ ] SDK
MaxUnpackAnyRecursionDepth = 10is not sufficient — semantic nesting (authz wrapping authz) must be handled by the ante handler itself[ ] Integration tests submit EVM messages wrapped in
MsgExecto verify they are validated
Mitigation:
// SECURE: Recurse into nested messages
func (d EthGasDecorator) checkMsg(msg sdk.Msg) error {
if ethMsg, ok := msg.(*evmtypes.MsgEthereumTx); ok {
return d.validateGas(ethMsg)
}
if exec, ok := msg.(*authz.MsgExec); ok {
for _, inner := range exec.Msgs {
var innerMsg sdk.Msg
if err := d.cdc.UnpackAny(inner, &innerMsg); err != nil {
return err
}
if err := d.checkMsg(innerMsg); err != nil {
return err
}
}
}
return nil
}References:
8 EVM HOOKS AND CROSS-MODULE INTERACTIONS
Description: Cosmos EVM chains use hooks (PostTxProcessing, PreTxProcessing) to synchronize EVM and Cosmos state after EVM transactions. The x/evm and x/erc20 modules fire these hooks on every EVM transaction. Bugs in hook logic — missing error propagation, state assumptions, reentrancy via hook callbacks — break the cross-module state invariant.
What to Check:
[ ] EVM hooks from
x/evmandx/erc20modules reviewed for correctness[ ] Hook errors propagate correctly (a failed hook must revert the entire EVM transaction)
[ ] Hooks do not assume EVM state is finalized (it may still be in a cached context)
[ ] Cross-module interactions between
x/evm,x/erc20,x/bank, andx/stakingdo not create circular dependencies or reentrancy[ ] User interaction with module special addresses reviewed — EVM contracts should not be able to interact with Cosmos module accounts in unexpected ways
9 ETHEREUM ADDRESS VALIDATION
Description: Cosmos EVM chains handle two address formats: Bech32 (cosmos1...) and hex (0x...). The go-ethereum function common.HexToAddress() does NOT validate format and silently ignores errors — it returns a zero-padded address for any invalid input. Address mapping between the two formats must be bijective and validated.
Detection Patterns:
// VULNERABLE: HexToAddress does not verify format, ignores errors
addr := common.HexToAddress(userInput)
// "not_an_address" → 0x0000000000000000000000000000000000000000
// No error returned! Silently maps to zero address.
// VULNERABLE: Inconsistent address derivation
cosmosAddr := sdk.AccAddress(ethAddr.Bytes()) // Cosmos addr from ETH addr
ethAddr2 := common.BytesToAddress(cosmosAddr) // Round-trip may not matchWhat to Check:
[ ] NOT using
common.HexToAddress()for user-supplied input — use a validating function instead[ ] Address conversion between Bech32 and hex is validated in both directions
[ ] Zero address (
0x000...) is explicitly rejected where it should not be valid[ ] EVM contract addresses derived from Cosmos module accounts are deterministic and documented
Mitigation:
// SECURE: Validate hex address format before conversion
func ValidateHexAddress(addr string) (common.Address, error) {
if !common.IsHexAddress(addr) {
return common.Address{}, fmt.Errorf("invalid hex address: %s", addr)
}
return common.HexToAddress(addr), nil
}10 CROSS-CHAIN TRANSACTION REPLAY AND CHAIN ID
Description: Cosmos EVM chains use an epoch-based chain ID format (chainname_NNNN-1) where the numeric part maps to the Ethereum chain ID used in EVM transaction signing. If the chain ID epoch is not correctly parsed or enforced, transactions signed for one chain can be replayed on another. Nonce management differs between EVM (per-account incrementing) and Cosmos (sequence numbers) — mismatches enable replay or stuck transactions.
What to Check:
[ ] Chain ID epoch is correctly parsed and used as the EVM chain ID in
ethermint/types/chain_id.go[ ] EVM transactions are validated against the current chain ID (not just any valid chain ID)
[ ] Nonce management is synchronized between EVM and Cosmos account sequences
[ ] Chain upgrades that change the chain ID epoch correctly invalidate pending EVM transactions
[ ]
eth_chainIdRPC returns the correct value matching the Cosmos chain ID epoch
SUMMARY: COSMOS EVM BUG CLASSES
| # | Pattern | Impact | Known Incident / Reference |
|---|---|---|---|
| 1 | EVM revert doesn't rollback Cosmos state | Fund theft (infinite delegation/mint) | Saga $7M, Finding 5/7 |
| 2 | StateDB.Commit() during precompile | Infinite mint (exponential) | Evmos, Finding 6 |
| 3 | Partial precompile execution (OOG) | Module drainage | GHSA-mjfq-3qr2-6g84, Finding 20 |
| 4 | Reverted EVM calls leave Cosmos msgs | Unauthorized IBC transfers | Initia, Finding 26b |
| 5 | Gas limit bypass via precompile dispatch | Reentrancy guard defeat | Initia, Finding 26c |
| 6 | Wrong routing in multi-denom ERC20 ops | Fund misrouting | Initia, Finding 26a |
| 7 | Ethermint ante handler bypass | Validation skip via nested msgs | Jump Crypto disclosure |
| 8 | EVM hooks cross-module interactions | State desync | Multiple chains |
| 9 | Ethereum address validation | Zero address injection | go-ethereum HexToAddress |
| 10 | Cross-chain tx replay / chain ID | Transaction replay | Chain ID epoch parsing |
GREP PATTERNS (ALL PATTERNS)
Use the Grep tool with glob: "*.go" in x/ for all searches below.
Precompile implementations:
func.*Run\(.*vm\.EVM— precompile Run methodsprecompile|Precompile— precompile files
StateDB usage:
StateDB|stateDB— then check results forcommitcontextstateDB\.(Snapshot|RevertToSnapshot)— snapshot/revert usage
EVM hooks:
PostTxProcessing|PreTxProcessing|EvmHooks
ERC20 module:
isERC20|IsERC20|erc20— ERC20 detectionRegisterERC20|RegisterCoin|ToggleConversion— token conversion
Address handling:
HexToAddress|IsHexAddress|BytesToAddress
Ante handlers:
AnteHandle|AnteDecorator— handler filesMsgEthereumTx|EthereumTx— EVM message type
Chain ID:
ChainID|ParseChainID|EpochNumber
Solidity patterns (use glob: "*.sol" in contracts/):
try|catch|revert— revert/try-catch patterns
References
Saga $7M exploit: https://advisories.gitlab.com/pkg/golang/github.com/cosmos/evm/GHSA-54gx-3cgr-7mfm/
Evmos infinite mint: https://blog.asymmetric.re/evmos-precompile-state-commit-infinite-mint/
EVM staking precompile: https://forum.cosmos.network/t/critical-evm-precompiled-contract-bug-allowing-unlimited-token-mint/14060
Partial precompile state writes: https://github.com/advisories/GHSA-mjfq-3qr2-6g84
Initia Code4rena: https://code4rena.com/reports/2025-02-initia-cosmos
Ethermint ante handler bypass: https://jumpcrypto.com/bypassing-ethermint-ante-handlers/
Evmos module accounts: https://docs.evmos.org/protocol/module-accounts
Evmos EVM hooks: https://docs.evmos.org/protocol/modules/evm#hooks
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.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download cosmos-vulnerability-scanner.zip