ADVANCED VULNERABILITY PATTERNS
> Updated for Cosmos SDK v0.53.x. Advanced infrastructure patterns (§24-27) covering storage key design, consensus validation gaps, circuit breaker authorization, and cryptographic verification. Referenced by [SKILL.md](../SKILL.md) checklist items via `§` anchors.
Overview
Advanced Vulnerability Patterns
Updated for Cosmos SDK v0.53.x. Advanced infrastructure patterns (§24-27) covering storage key design, consensus validation gaps, circuit breaker authorization, and cryptographic verification. Referenced by SKILL.md checklist items via
§anchors.
24 STORAGE KEY DESIGN FLAWS
Description: KV store key construction allows collisions, prefix overlaps, or unauthorized access to other users' data. Keys built via string concatenation without length-prefixed encoding create ambiguous boundaries. Numeric fields serialized as variable-length strings share byte prefixes. Prefix iterators over attacker-controllable components include unintended objects.
Detection Patterns:
Pattern 1: String Concatenation Collision
// VULNERABLE: String concatenation without length-prefix encoding (Finding 16)
func VaultUserKey(vaultID uint64, user string) []byte {
return []byte(fmt.Sprintf("%d%s", vaultID, user))
}
// vaultId=1, user="2a" → key "12a"
// vaultId=12, user="a" → key "12a" ← COLLISION!
// Even with separators, string fields containing the separator collide:
// vaultId=1, user="a|", position="b" → "1|a||b"
// vaultId=1, user="a", position="|b" → "1|a||b" ← COLLISION!
Pattern 2: Prefix Iterator Malleability
// VULNERABLE: Pool IDs serialized as strings in KV store keys (Finding 31)
func PositionKey(poolID uint64, posID uint64) []byte {
return []byte(fmt.Sprintf("position/%d/%d", poolID, posID))
}
// poolID=42 shares byte prefix [52 50] with poolID=421
// HasAnyAtPrefix("position/42") matches "position/421/..."
// Attacker creates positions with carefully chosen pool IDs
// that prefix-collide with victim positions
// VULNERABLE: Attacker can increment pool IDs by creating empty pools
store.Iterator(PositionPrefix(poolID), nil) // Includes poolID * 10 + N
Pattern 3: Redelegation State Lookup Bypass
// VULNERABLE: Unbonding period bypass via redelegation (Finding 14)
// If a user redelegates to a validator outside the active set
// (e.g., bonded stake rank 102), then unbonds, the 21-day
// unbonding period is skipped — the state key lookup for the
// unbonding entry doesn't account for out-of-set validators.
// Attacker who steals tokens can instantly exit staking.
What to Check:
- KV store keys use length-prefixed encoding (e.g.,
collections.PairKeyCodec,address.LengthPrefixedAddressKey) — NOT string concatenation - Prefix iterators cannot be confused by attacker-controllable numeric IDs (e.g., pool IDs serialized as fixed-width or length-prefixed)
- String fields in composite keys cannot contain the separator character
-
HasAnyAtPrefix/Iteratorwith user-influenced prefix components verified to not match unintended keys - Redelegation to out-of-set validators followed by unbonding is tested against unbonding period enforcement
Mitigation:
// SECURE: Use length-prefixed encoding
func VaultUserKey(vaultID uint64, user string) []byte {
key := make([]byte, 8+1+len(user))
binary.BigEndian.PutUint64(key[:8], vaultID) // Fixed-width
key[8] = byte(len(user)) // Length prefix
copy(key[9:], user)
return key
}
// SECURE: Use cosmossdk.io/collections key codecs
var PositionsMap = collections.NewMap(
sb, PositionsPrefix,
collections.PairKeyCodec(collections.Uint64Key, collections.Uint64Key),
codec.CollValue[Position](cdc),
)
References:
- Zellic — Cosmos Security Primer (Finding 16)
- Fault Tolerant — Cosmos Security Handbook (Finding 31)
- Node A-Team — Unbonding Period Bypass (Finding 14)
25 CONSENSUS VALIDATION GAPS
Description: Missing or incomplete validation at the consensus protocol layer — vote extensions, block timestamp manipulation, and account type checks at creation time. These allow Byzantine validators or front-running attackers to manipulate consensus-level data that downstream modules trust implicitly.
Detection Patterns:
Pattern 1: Vote Extension Forgery
// VULNERABLE: PrepareProposal validates signatures but skips
// VerifyVoteExtension rules (Finding 15, SEDA Chain)
func (h *Handler) PrepareProposal(ctx sdk.Context, req *abci.PrepareProposalRequest) (*abci.PrepareProposalResponse, error) {
for _, vote := range req.LocalLastCommit.Votes {
// Only checks vote signature — does NOT re-run VerifyVoteExtension!
if !verifySignature(vote) { continue }
// CometBFT accepts late precommits without full verification
// Byzantine validator smuggles malicious vote extension into proposal
extensions = append(extensions, vote.VoteExtension)
}
// Leader unknowingly includes forged oracle data in block
}
Pattern 2: Block Timestamp Manipulation
// VULNERABLE: Any module trusting ctx.BlockTime() for security-critical decisions
// CometBFT "Tachyon" (Finding 21, CSA-2026-001): inconsistency between how
// commit signatures are verified and how block time is derived breaks BFT Time
// guarantee. A faulty process CAN arbitrarily increase block time.
// These are all exploitable if block time can be manipulated:
deadline := proposal.SubmitTime.Add(votingPeriod)
if ctx.BlockTime().After(deadline) { ... } // Governance deadlines
vestingEnd := account.EndTime
if ctx.BlockTime().After(vestingEnd) { ... } // Vesting unlocks
price := oracle.GetTWAP(ctx, pair, window) // Time-weighted prices
Pattern 3: Vesting on Blocked/Module Addresses
// VULNERABLE: Creating vesting accounts on blocked addresses (Finding 29, ASA-2024-003)
func (ms msgServer) CreatePeriodicVestingAccount(ctx context.Context, msg *types.MsgCreatePeriodicVestingAccount) error {
toAddr := sdk.AccAddressFromBech32(msg.ToAddress)
// Missing: check if toAddress is a blocked address or module account
// Funds deposited to module accounts via vesting become permanently inaccessible
// Could also enable unintended interactions with module accounts
}
What to Check:
-
PrepareProposalre-validates vote extensions using the same rules asVerifyVoteExtension— do NOT trust that CometBFT has already validated them (late precommits may skip verification) -
ProcessProposalindependently validates all vote extensions included in the proposal - Vote extension content is treated as untrusted input even from non-Byzantine validators
- CometBFT is patched for Tachyon (>= v0.38.21 / v0.37.18) — block timestamp manipulation
- Time-dependent logic (governance deadlines, vesting unlocks, oracle TWAPs, lockup periods, auction timers) is reviewed against block time manipulation
-
CreateVestingAccount/CreatePeriodicVestingAccountreject blocked addresses and non-initialized module accounts as recipients - SDK version includes Barberry patches (>= v0.46.13, >= v0.47.3) for vesting on blocked addresses
References:
- OtterSec — SEDA Vote Extension Forgery (Finding 15)
- CometBFT Tachyon CSA-2026-001 (Finding 21)
- ASA-2024-003 Vesting on Blocked Addresses (Finding 29)
26 CIRCUIT BREAKER AUTHZ BYPASS
Description: The x/circuit module allows authorized accounts to disable (circuit-break) specific message types during incident response. An authorization scope bug allows an account authorized to circuit-break specific message types to reset the circuit breaker for any message type, even those it lacks permission for. This can re-enable message types intentionally disabled for security.
Detection Patterns:
// VULNERABLE: Circuit breaker reset does not verify per-type authorization
// Account authorized to break MsgSend can reset MsgDelegate
func (ms msgServer) ResetCircuitBreaker(ctx context.Context, msg *types.MsgResetCircuitBreaker) error {
// Checks: is the sender an authorized circuit breaker account? YES
// Missing: does the sender have authority over THIS SPECIFIC message type?
// Result: any circuit breaker account can re-enable any disabled message type
}
What to Check:
-
x/circuitreset/authorize operations validate per-message-type permissions (not just "is this a circuit breaker account") - Circuit breaker accounts have minimal scope — only the message types they are responsible for
- Re-enabling a circuit-broken message type requires the same or higher authorization level as disabling it
- During incident response, monitor for unexpected circuit breaker resets
Grep Patterns (use Grep tool):
circuit|CircuitBreaker|ResetCircuitBreakerwithglob: "*.go"inx/x/circuitwithglob: "*.go"inapp/
References:
- HackerOne Report 2120609 (Finding 28)
27 MERKLE PROOF / CRYPTOGRAPHIC VERIFICATION FLAWS
Description: Specification-level or implementation-level flaws in cryptographic proof systems allow forging proofs that should be computationally infeasible. ICS-23 was unsound at the spec level (not just implementation). Cross-chain bridges that verify proofs differently on each side create asymmetric validation. This class has caused the largest single loss in the Cosmos ecosystem ($566M BNB bridge exploit, related flaw).
Detection Patterns:
Pattern 1: ICS-23 Absence Proof Forgery
// VULNERABLE: ICS-23 spec allowed proofs to carry their own spec parameters
// (Finding 3a, Dragonberry — all IBC chains, all versions)
// Validation only required input leaf prefix to CONTAIN the standard spec's
// prefix as a substring — not match exactly.
// Attacker forges absence proof to "prove" packet commitment doesn't exist
// on counterparty chain (even though it does), then triggers IBC timeout refunds
// for packets that were successfully delivered.
// Iterative drainage of ALL ICS-20 escrow accounts across ALL Cosmos networks.
Pattern 2: IAVL RangeProof Forgery
// VULNERABLE: Same class as Dragonberry in IAVL RangeProof system
// (Finding 30, Dragonfruit — $566M BNB bridge exploit used related flaw)
// Missing validation checks on leaf/inner-node prefix/suffix length
// allowed forging proofs against the IAVL Merkle tree.
Pattern 3: ECDSA Signature Malleability
// VULNERABLE: Cosmos-side ECDSA verification accepts non-canonical s-values
// (Finding 13, Gravity Bridge)
func EthAddressFromSignature(hash []byte, sig []byte) (common.Address, error) {
// Missing: canonical s-value validation
// ECDSA has two valid s values per signature (elliptic curve symmetry)
// Ethereum enforces s < secp256k1.N/2, Cosmos side does not
// Malicious orchestrator submits non-canonical signature:
// Cosmos accepts → Ethereum rejects → bridge enters crash loop
// Selectively block validator set updates and transaction batches
}
What to Check:
- ICS-23 proof verification uses patched libraries — leaf prefix must match exactly, not just contain as substring
- IAVL library patched for Dragonfruit (prefix/suffix length validation on inner/leaf nodes)
- ECDSA signature verification enforces canonical s-value (
s < secp256k1.N/2) on BOTH sides of any bridge - Cross-chain proof verification is symmetric — same validation on source and destination
- Custom Merkle proof implementations do not allow proof parameters to be attacker-controlled
- If using BLS, ed25519, or other signature schemes for cross-chain verification, signature malleability is addressed
Grep Patterns (use Grep tool with glob: "*.go" in x/):
VerifyMembership|VerifyNonMembership|VerifyProof— proof verificationRangeProof|InnerNode|LeafNode— Merkle tree operationsEthAddressFromSignature|RecoverPubkey|Ecrecover— signature recovery
References:
- Dragonberry / ICS-23 Proof Forgery (Finding 3a)
- Verichains ICS-23 analysis
- Dragonfruit / IAVL RangeProof (Finding 30)
- Gravity Bridge ECDSA malleability (Finding 13)