# SPV Merkle Proof Duplication Allows Forged Transaction Inclusion

BitcoinLightClient's SPV verifier omitted an index bounds check, letting a valid Merkle proof be reused with index+2^depth to forge phantom deposits and drain the Citrea bridge.

- Payout: Contest reward
- Severity: medium
- Program: Citrea
- Credited: Codertjay
- Disclosed: 2025-10-21
- Write-up quality: 8/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_citrea_jul2025.pdf#3.2.1
- Library page: https://bugbountylibrary.com/r/cantina%3Acitrea_jul2025%3A3.2.1
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:signature-replay

**Target:** BitcoinLightClient.sol verifyInclusion / ValidateSPV.prove SPV Merkle verifier
**Root cause:** ValidateSPV.prove navigates the Merkle path using only the low bits of _index and never enforces _index < 2**treeDepth, so indices differing by 2**h produce identical verification paths.
**Steps:**
1. Initialize block number and set block info with root `DBEE9A86...FDBF2` and treeDepth=2 (tree size 4).
2. Build proof array of 2 hashes: `0x00...00` and `6B1DAB57...1628D` for wtxId `A28E549D...0087`.
3. Call `verifyInclusion(blockHash, wtxId, proof, 1)` — returns true (legit).
4. Call `verifyInclusion(blockHash, wtxId, proof, 1 + (1<<2) = 5)` — also returns true, proving out-of-bounds index accepted.
5. Run `forge test --mt test_Vulnerability_MerkleProofDuplication` to confirm.
**How found:** Code review of verifyInclusion's delegation to ValidateSPV.prove; noticed the library validates the cryptographic path but never checks _index against 2**_proof.length, and that path navigation uses only low index bits.
**Exploitation:** Attacker observes a small real Bitcoin deposit, captures its valid Merkle proof, then submits a bridge deposit claim with a fabricated txId and index = realIndex + 2**treeDepth; the contract accepts the proof, mints unbacked assets, and drains bridge liquidity.
**Escalation:** Phantom deposit mints unbacked assets enabling full liquidity drain and breaking the rollup's L1-anchored security guarantee.
**Insight:** When auditing SPV/Merkle verifiers, check that the leaf index is bounds-checked against 2**proof.length — libraries that walk the path bit-by-bit silently accept index + k*2^h, so any proof can be replayed for a phantom leaf.
