Cross-Chain Balance Inconsistency Exploit Generator
Sigvex exploit generator that validates cross-chain bridge balance inconsistency vulnerabilities including replay attacks, non-atomic operations, weak proof verification, and finality exploits that allow token supply inflation on the destination chain.
Cross-Chain Balance Inconsistency Exploit Generator
Overview
The cross-chain balance inconsistency exploit generator validates findings from the cross_chain_balance, bridge_balance, and related detectors by simulating an attack where 1000 tokens are burned on the source chain but 2000 tokens are minted on the destination chain — a 2x supply inflation. The generator classifies which specific atomicity gap caused the inconsistency (replay, non-atomic operations, weak verification, or finality exploit) and generates a targeted proof of concept.
Cross-chain bridge balance inconsistencies have produced some of the largest losses in DeFi history. The Poly Network lost $611M when attackers bypassed its cross-chain proof verification. The Wormhole Bridge lost $326M from a signature verification bypass. The Nomad Bridge lost $190M when an initialization bug treated all messages as pre-verified. In each case, the fundamental invariant — totalSupply(destination) == totalLocked(source) — was violated.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
Replay attack (missing proof deduplication):
- A user burns 1000 tokens on the source chain.
- A relayer submits the burn proof to the destination chain, which mints 1000 tokens.
- The destination bridge does not track which proofs have been used.
- The attacker re-submits the same burn proof 10 times.
- The destination mints 10,000 tokens total from a single 1000-token burn.
Non-atomic operations (mint before confirmed burn):
- A bridge emits a
BurnRequestedevent before actually burning tokens. - A relayer sees the event and mints on the destination.
- The source chain burn transaction runs out of gas or reverts after the event was emitted.
- Tokens were minted on the destination but never burned on the source.
- The user holds tokens on both chains simultaneously.
Weak proof verification:
- The destination bridge requires
proof.length > 0as its only validation. - An attacker submits
bytes("fake")as the proof. - The bridge mints tokens without any cryptographic verification of a source chain burn.
- Repeated 100 times, the attacker inflates supply by 100,000 tokens from zero burns.
Finality exploit (chain reorganization):
- A user burns tokens on the source chain.
- The destination bridge uses only 1 confirmation block before minting.
- The source chain reorganizes — the burn transaction is removed from the canonical chain.
- The destination’s mint remains because it confirmed before the reorg propagated.
- The user holds tokens on both chains.
Exploit Mechanics
The generator simulates the balance inconsistency mathematically:
source_burned = 1000 tokens (1000 * 1e18 units)
dest_minted = 2000 tokens (2000 * 1e18 units)
supply_inflation = 1000 tokens
It classifies the vulnerability type from the finding description:
| Description contains | Vulnerability type | Severity |
|---|---|---|
replay | replay_attack | CRITICAL: Unlimited minting via replay |
atomic, atomicity | non_atomic_bridge | CRITICAL: Double-spend tokens across chains |
verify, proof | weak_verification | CRITICAL: Unlimited inflation via forged proofs |
finality | finality_exploit | HIGH: Finality delays enable double-spend |
| (other) | balance_inconsistency | HIGH: Supply inconsistency across chains |
Estimated gas: 80,000.
// VULNERABLE: No replay protection
contract VulnerableBridgeReplay {
mapping(address => uint256) public balances;
function mintFromBridge(address to, uint256 amount, bytes32 burnProof) external {
require(burnProof != bytes32(0), "Invalid proof");
// VULNERABILITY: burnProof can be submitted multiple times!
balances[to] += amount;
}
}
// Exploit: Submit same proof 10 times → 10x minting
contract ReplayExploit {
function attack(bytes32 legitimateBurnProof) external {
for (uint i = 0; i < 10; i++) {
bridge.mintFromBridge(msg.sender, 1000 ether, legitimateBurnProof);
}
// Burned 1000 tokens, minted 10,000 → 9000 inflation
}
}
// SECURE: Proof deduplication + deep finality + merkle verification
contract SecureBridge {
mapping(bytes32 => bool) public usedProofs;
uint256 public constant MIN_CONFIRMATIONS = 64; // Deep finality
function mintFromBridge(
address to, uint256 amount, bytes32 burnTxHash, uint256 sourceBlockNumber,
bytes32[] memory merkleProof, bytes memory validatorSignatures
) external {
bytes32 proofHash = keccak256(abi.encodePacked(burnTxHash, to, amount, sourceBlockNumber));
// 1. Replay protection
require(!usedProofs[proofHash], "Proof already used");
// 2. Finality check (64 blocks for Ethereum mainnet)
require(block.number >= sourceBlockNumber + MIN_CONFIRMATIONS, "Insufficient confirmations");
// 3. Cryptographic proof verification
require(verifyMerkleProof(merkleProof, burnTxHash), "Invalid merkle proof");
// 4. Validator signature verification
require(verifyValidatorSignatures(validatorSignatures, proofHash), "Invalid signatures");
usedProofs[proofHash] = true;
balances[to] += amount;
}
}
Remediation
- Detector: Cross-Chain Balance Detector
- Remediation Guide: Cross-Chain Balance Remediation
Address each attack vector with a dedicated mitigation:
-
Replay protection: Track all processed proof hashes in a mapping. Use
bytes32 proofHash = keccak256(abi.encodePacked(burnTxHash, to, amount, sourceBlockNumber))as the deduplication key. -
Atomic operations: Use lock-and-mint rather than burn-and-mint where possible. Lock tokens on the source (recoverable if mint fails) rather than burning them irrecoverably before the mint is confirmed.
-
Deep finality: Require a minimum of 64 block confirmations on Ethereum mainnet before processing a mint. For probabilistic finality chains, use a conservative estimate of the orphan risk.
-
Cryptographic proof verification: Implement Merkle proof verification of the source chain burn transaction. Require signatures from at least 2/3 of an independent validator set.
-
Balance reconciliation monitoring: Continuously monitor
totalSupply(destination) == totalLocked(source). Emit alerts and trigger circuit breakers if the invariant is violated. -
Transfer limits: Enforce per-transaction and per-period maximum transfer amounts. Large transfers require longer timelocks.