DEV Community

Saravana kumar for Cryip

Posted on • Originally published at cryip.co

Polkadot Bridge Hack: MMR Proof Bug Leads to 1B DOT Mint

On April 13, 2026, the Hyperbridge ISMP (Interoperability State Machine Protocol) gateway on Ethereum was exploited. An attacker forged an ISMP PostRequest by exploiting a critical edge-case bug in the Merkle Mountain Range (MMR) proof verification logic combined with missing proof-to-request binding and weak authorization checks in the TokenGateway contract.
The result: 1,000,000,000 bridged DOT tokens were minted in a single atomic transaction, which were immediately swapped for approximately 108.2 ETH ($237,000 – $242,000). Native Polkadot remained completely unaffected. The EthereumHost contract has since been frozen.

On-Chain Summary

Exploit Transaction: 0x240aeb9a8b2aabf64ed8e1e480d3e7be140cf530dc1e5606cb16671029401109
Attacker EOA: 0xC513E4f5D7a93A1Dd5B7C4D9f6cC2F52d2F1F8E7
Master Contract: 0x518AB393c3F42613D010b54A9dcBe211E3d48f26
Helper Contract: 0x31a165a956842aB783098641dB25C7a9067ca9AB
Target Token: 0x8d010bf9C26881788b4e6bf5Fd1bdC358c8F90b8 (Bridged DOT – ERC-6160)
Profit: ~108.2 ETH
Gas Used: ~0.000339 ETH

Root Cause Analysis

The exploit was made possible by a dangerous combination of three vulnerabilities:
1. MMR Library Edge-Case Bug
The Merkle Mountain Range library contained a boundary-condition flaw in leavesForSubtree() and CalculateRoot(). When leafCount == 1, supplying an out-of-range leaf_index (e.g., 1) caused the function to silently drop the forged leaf. The verifier then promoted the next element in the proof array , a stale but legitimate historical root, directly to the computed root position.
This allowed the system to accept a completely forged payload while still passing proof verification.
Fix:
solidity
function leavesForSubtree(uint256 leafCount, uint256 leafIndex) internal pure returns (uint256) {
if (leafIndex >= leafCount) {
revert InvalidLeafIndex(leafIndex, leafCount);
}
// existing logic
}

function CalculateRoot(...) public pure returns (bytes32) {
require(leafIndex < leafCount, "MMR: leaf index out of bounds");
// ...
}
Recommendation: Always add strict bounds checking and unit tests for edge cases where leafCount == 0 or leafCount == 1.

2. Missing Cryptographic Binding Between Proof and Request

HandlerV1 only checked that the request commitment hash (request.hash()) had not been consumed before. However, the proof verification did not cryptographically bind the submitted request payload to the validated MMR proof.
As a result, an attacker could pair any valid historical proof with a completely different malicious request body.
Fix:
solidity
function handlePostRequests(PostRequest calldata request, bytes[] calldata proof) external {
// Bind proof to the exact request
bytes32 commitment = keccak256(abi.encode(request, proof));
require(!consumed[commitment], "ISMP: already consumed");

require(verifyProof(request, proof), "ISMP: invalid proof");
// ...
Enter fullscreen mode Exit fullscreen mode

}
3. Weak Authorization in TokenGateway
Governance actions in TokenGateway used only a shallow source field check instead of the full authenticate(request) modifier:
solidity
function handleChangeAssetAdmin(PostRequest calldata request) internal {
if (!request.source.equals(IIsmpHost(_params.host).hyperbridge()))
revert UnauthorizedAction();

// CRITICAL: authenticate(request) was missing
IERC6160Ext20(erc6160Address).changeAdmin(newAdmin);
Enter fullscreen mode Exit fullscreen mode

}
Additionally, the challengePeriod was set to 0, removing any delay-based safety window.
Fix:
solidity
function handleChangeAssetAdmin(PostRequest calldata request) internal {
authenticate(request); // Full authentication
require(challengePeriod > 0, "Challenge period must be enabled");

IERC6160Ext20(erc6160Address).changeAdmin(newAdmin);
Enter fullscreen mode Exit fullscreen mode

}
4. Dangerous ERC-6160 Privilege Model
The ERC-6160 token standard granted the new admin immediate and unrestricted MINTER_ROLE and BURNER_ROLE upon calling changeAdmin(). There was no time-lock, multi-signature requirement, or secondary confirmation.
Once the attacker’s helper contract became the admin, it could mint 1 billion DOT tokens in a single call.
Fix Recommendations:

Step-by-Step Attack Flow

  1. The attacker funded the EOA through Railgun shielded pools and Synapse Bridge for obfuscation.
  2. Deployed a master orchestration contract and a helper contract (which would become the new token admin).
  3. Called HandlerV1.handlePostRequests() with a carefully crafted PostRequest and MMR proof that triggered the leafCount == 1 edge case.
  4. The forged request (action 0x04 – ChangeAssetAdmin) was dispatched to TokenGateway.onAccept().
  5. The helper contract was set as the new admin of the bridged DOT token.
  6. 1 billion DOT tokens were minted, approved to OdosRouterV3, and swapped via Uniswap V4 PoolManager for 108.2 ETH in one transaction.

Key Lessons & Developer Checklist

1. Risk: MMR Proof
Vulnerability: Edge case when leafCount == 1
Recommended Fix: Enforce strict validation (leafIndex < leafCount) and add thorough tests
Priority: Critical
2. Risk: Proof Handling
Vulnerability: No binding between proof and request
Recommended Fix: Use a cryptographic commitment over (request + proof)
Priority: Critical
3. Risk: Authorization
Vulnerability: Only a shallow source check is performed
Recommended Fix: Implement full authenticate(request) modifier
Priority: Critical
4. Risk: Governance
Vulnerability: challengePeriod = 0
Recommended Fix: Enforce a minimum delay of 1 hour
Priority: High
5. Risk: Token Admin
Vulnerability: Instant minting rights
Recommended Fix: Add time-lock and separate role management
Priority: High
6. Risk: Architecture
Vulnerability: Single gateway handling all assets
Recommended Fix: Split into separate TokenGateway per asset
Priority: Medium

Additional Best Practices:

Implement real-time monitoring for large mints from the zero address.
Conduct thorough audits of light clients and consensus integrations.
Run a generous bug bounty program focused on proof verification paths.
Always test boundary conditions in Merkle-based verification logic.

Final Thoughts

This exploit highlights a critical truth in cross-chain bridge security: proof verification and authorization must both be bulletproof. A single weakness in either layer can lead to catastrophic consequences.
For developers building bridges, light clients, or token gateways:
Never skip strict bounds checking.
Always cryptographically bind proofs to their payloads.
Treat governance actions with the same (or higher) security standards as asset transfers.

Top comments (0)