- name:
- cross-chain-bridge-vulnerabilities
- description:
- Hardcoded bridge or relay address - if the bridge contract is upgraded or redeployed, this contract cannot adapt without redeployment
- category:
- vulnerability-pattern
- pattern_category:
- logic-error
- - regex:
- address\s+(constant|immutable)\s+\w*(bridge|Bridge|BRIDGE|relay|Relay|messenger|Messenger)\w*\s*=
- severity:
- Medium
- confidence:
- High
Cross-Chain Bridge Vulnerability Patterns
Overview
Bridge systems expand trust boundaries across chains, consensus assumptions, and message formats. A single validation error in message authentication can mint unbacked assets, unlock escrowed collateral, or allow arbitrary calls on destination chains. Because bridges often custody large TVL, exploit impact is frequently catastrophic.
Two themes dominate bridge incidents: insufficient domain separation and weak message authenticity checks. Domain separation prevents a proof or signature from one context (chain, contract, epoch) from being reused in another. Authenticity checks ensure only approved bridge infrastructure and source identities can trigger state transitions.
Bridge security review should treat every inbound message as adversarial by default. Validation must bind the message to source chain, source sender, destination chain, destination contract, nonce, and replay state. Any omitted field becomes a likely replay or forgery surface.
Key Attack Vectors
- Message hash construction that omits
chainId or equivalent domain fields.
- Signature verification via
ecrecover without chain-specific binding.
- Receiver handlers that trust
msg.sender without verifying authorized bridge endpoint.
- Missing validation of source chain and source application address.
- Replayable messages due to absent nonce consumption or idempotency checks.
- Hardcoded bridge addresses that become stale after upgrades or migrations.
- Weak upgrade controls on bridge config, relayers, and validator sets.
- Message parsers that decode calldata but do not enforce strict schema/version.
Typical Replay Attack Flow
- Attacker observes a valid signed bridge message on Chain A -> Chain B.
- Message does not include robust domain separation fields.
- Attacker replays the same payload on another deployment or fork.
- Destination contract accepts the message as valid.
- Funds are minted or released multiple times.
- Accounting diverges from source-chain lock state.
Typical Authentication Bypass Flow
- Bridge receiver exposes
handleBridgeMessage style function.
- Function checks payload structure but not trusted caller/source identity.
- Attacker calls function directly with crafted message.
- Contract executes privileged state change (mint, transfer, config update).
- Attack completes without compromising bridge validators.
Detection Heuristics
Domain Separation Checks
- Search message hash construction for inclusion of
block.chainid or canonical chainId field.
- Confirm hash binds destination contract address and source chain identifiers.
- Verify signatures use EIP-712 domain separators with
chainId and verifyingContract.
- Flag ad-hoc
abi.encodePacked payloads with ambiguous or incomplete fields.
Signature Verification Checks
- Review
ecrecover call sites for explicit domain-bound message digests.
- Ensure recovered signer is validated against current authorized signer set.
- Check for malleability handling and strict
s value constraints where needed.
- Confirm nonce or message ID is consumed exactly once.
Receiver Authorization Checks
- Require
msg.sender == trustedBridge or equivalent allowlist enforcement.
- Validate source chain ID and source sender embedded in payload.
- Confirm message ordering and replay protection against duplicate IDs.
- Ensure receiver functions are
nonReentrant if they trigger external calls.
Configuration and Upgrade Checks
- Flag immutable or constant bridge addresses for systems that expect migrations.
- Validate admin setter functions are timelocked and role-gated.
- Check event emissions for all config changes (bridge, relayer, validator set).
- Review emergency pause controls and recovery workflows.
Concrete Code Smells
bytes32 digest = keccak256(abi.encodePacked(amount, recipient, nonce));
address signer = ecrecover(digest, v, r, s); // no chain binding
function handleBridgeMessage(bytes calldata payload) external {
// missing require(msg.sender == trustedBridge)
_process(payload);
}
address immutable bridgeMessenger = 0x1234...; // no upgrade path
Audit Checklist
- Is message identity globally unique across chains and contracts?
- Can the same proof be replayed on forks or sibling deployments?
- Are source app addresses validated against chain-scoped allowlists?
- Is every successful message marked consumed atomically?
- Can governance safely rotate bridge endpoints and signer sets?
Prevention
Message Schema Hardening
- Use typed message structs with explicit fields: source chain, destination chain, source app, destination app, nonce, payload hash.
- Hash using EIP-712 domain separation when signatures are involved.
- Reject unknown schema versions to avoid parsing ambiguity.
- Enforce strict decoding with size and range checks.
Authentication and Replay Controls
- Verify caller is the designated bridge endpoint contract.
- Validate source chain ID and sender against immutable or governable allowlists.
- Consume message IDs in a replay map before external side effects.
- Make message execution idempotent where practical.
Configurability with Safety
- Prefer configurable bridge addresses over hardcoded constants.
- Protect config updates with timelock and multi-sig governance.
- Emit detailed events on every trust-boundary change.
- Add two-step ownership transfer for bridge admin roles.
Hardened Receiver Example
function handleBridgeMessage(
uint256 sourceChainId,
address sourceApp,
uint256 nonce,
bytes calldata payload,
bytes calldata proof
) external nonReentrant {
require(msg.sender == trustedBridge, "Unauthorized bridge caller");
require(allowedSourceChains[sourceChainId], "Unsupported source chain");
require(allowedSourceApps[sourceChainId][sourceApp], "Unsupported source app");
bytes32 messageId = keccak256(
abi.encode(
block.chainid,
sourceChainId,
sourceApp,
address(this),
nonce,
keccak256(payload)
)
);
require(!consumed[messageId], "Replay");
require(verifyProof(messageId, proof), "Invalid proof");
consumed[messageId] = true;
_executePayload(payload);
}
Operational Defenses
- Continuously monitor duplicate message IDs across chains.
- Run chaos tests with forked deployments and stale bridge configs.
- Maintain emergency pause for inbound message processing.
- Reconcile bridge accounting between lock and mint sides on a schedule.
Real-World Examples
Wormhole (2022)
- Reference: https://rekt.news/wormhole-rekt/
- Forged verification path enabled minting of unbacked wrapped assets.
- Lesson: proof and signature validation must be strict, domain-separated, and invariant-tested.
Nomad (2022)
- Reference: https://rekt.news/nomad-rekt/
- Message validation assumptions failed, enabling widespread unauthorized message replay/copycat draining.
- Lesson: receiver authenticity checks and replay protection are critical at every handler entry point.
Additional Bridge Incident Patterns
- Bridge key-management failures (validator compromise).
- Config drift between source and destination chain deployments.
- Insufficient upgrade controls introducing unreviewed trust paths.
Pattern-to-Impact Mapping
missing-chain-id-validation -> cross-chain replay of otherwise valid messages.
replay-across-chains -> signature reuse on forks/L2 mirrors.
unverified-bridge-message -> direct unauthorized execution on destination chain.
hardcoded-bridge-address -> operational failure or unsafe hotfix pressure during upgrades.
References
- Rekt News Wormhole: https://rekt.news/wormhole-rekt/
- Rekt News Nomad: https://rekt.news/nomad-rekt/
- EIP-712 typed structured data hashing: https://eips.ethereum.org/EIPS/eip-712
- OpenZeppelin access control patterns: https://docs.openzeppelin.com/contracts/4.x/access-control
- Chainlink CCIP security overview: https://docs.chain.link/ccip
- NIST guidance on replay resistance concepts: https://csrc.nist.gov/
- Trail of Bits bridge security research: https://blog.trailofbits.com/