# Premature sending of disprove transactions allows malicious operator to steal bridge funds

Clementine challenger sends Disprove before operator challenge acks arrive; tx invalid, never retried, letting malicious operator reclaim funds via DisproveTimeout.

- Payout: Contest reward
- Severity: high
- Program: Clementine
- Credited: n4nika
- Disclosed: 2025-10-29
- Write-up quality: 7/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_clementine_aug2025.pdf#3.1.2
- Library page: https://bugbountylibrary.com/r/cantina%3Aclementine_aug2025%3A3.1.2
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:solidity technique:source-review vuln:business-logic vuln:race-condition

**Target:** Bridge challenger state machine (disprove_if_ready) on Clementine BitVM bridge
**Root cause:** disprove_if_ready only checks operator_asserts, latest_blockhash and spent watchtower UTXOs, not receipt of operator_challenge_acks, so Disprove is built before the operator's preimage/acks exist and is invalid onchain.
**Steps:**
1. Operator sends malicious kickoff; kickoff gets challenged and watchtowers send challenge txs.
2. Operator sends all asserts and the last blockhash but withholds challenge acknowledgements.
3. disprove_if_ready conditions are met, so challenger constructs and queues the Disprove tx once.
4. Disprove is invalid onchain (missing acks/preimage) and is not included; challenger state machine never retries.
5. Operator then sends acknowledgements, avoiding slashing.
6. After DISPROVE_TIMEOUT_TIMELOCK expires, operator sends DisproveTimeout and is reimbursed for the malicious kickoff.
**How found:** Code review of disprove_if_ready: noticed its guard omits operator_challenge_acks while the Disprove requires them, and that the challenger state machine sends Disprove only once, so a premature invalid send is never retried.
**Exploitation:** Malicious operator withholds OperatorChallengeAck txs until the challenger has already fired its single Disprove attempt; the invalid Disprove never lands, operator acks late to avoid slashing, then claims DisproveTimeout to recover funds from a malicious kickoff.
**Insight:** When auditing state machines that fire an action once, check every precondition the onchain tx actually needs against the trigger guard; a missing ack/preimage condition turns a one-shot send into a permanent no-op the adversary can time around.
