Worked example · 2022-01
Qubit Bridge — BNB Smart Chain ↔ Ethereum — 2022-01-27
Summary
On January 27, 2022, an attacker exploited a logic flaw in the QBridge contract (the cross-chain bridge between BNB Smart Chain and Ethereum operated by Qubit Finance, a BSC-side lending protocol). The QBridge contract exposed a deposit() function that accepted an arbitrary token address argument plus a resourceID. To support "deposit native ETH" semantics, the contract treated address(0) as a sentinel value indicating "the user is depositing native ETH" — the canonical pattern used by many bridges of the period.
The defect: the deposit() function accepted the address(0) sentinel and credited the user with bridged collateral (qXETH on the BSC side) without verifying that any ETH had actually been transferred along with the call. The dedicated depositETH() function was the path that was supposed to be used for native ETH deposits, and that function did include msg.value validation. But the attacker called deposit() directly, passing address(0) as the token address, with no ETH attached. The contract emitted a Deposit event indistinguishable from a real ETH-side deposit, the bridge's relayer picked it up, and the BSC side minted approximately 206,809 qXETH (~$80M at the time) to the attacker's address.
The attacker then deposited the freshly-minted qXETH as collateral into the Qubit lending market and borrowed out the entire pool of available liquid assets — BNB, BUSD, CAKE, MDX, USDT, ETH, BTCB, DOT — leaving the lending market technically insolvent against the synthetic collateral. The borrowed assets were swapped, bridged back to Ethereum, and routed through Tornado Cash.
For OAK's purposes, Qubit is the canonical worked example of the "0x0 deposit acceptance" subclass of T10.002. The defect is structurally adjacent to the Wormhole "missing guardian validation" pattern (examples/2022-02-wormhole.md) but distinct in shape: Wormhole's verification function failed to validate the authority of the message; Qubit's verification function failed to validate that an asset transfer underlay the deposit event. Both ladder up to T10.002 (operational classification: bridge with a verification flaw) and T9.004 (mechanistic classification: input that should have been treated as privileged is instead accepted unconditionally).
Timeline (UTC)
| When | Event | OAK ref |
|---|---|---|
| Pre-event | QBridge deposit() exposes an address(0) sentinel path for native-ETH deposits; the path does not validate msg.value > 0. The dedicated depositETH() does include the validation, but deposit() is reachable independently |
T10.002 / T9.004 latent flaw |
| 2022-01-27 (T+0) | Attacker calls deposit() on the Ethereum-side QBridge contract with token = address(0) and amount set to a large value, transferring no ETH; the contract emits a Deposit event as if a real native-ETH deposit had occurred |
T10.002 message forgery |
| 2022-01-27 (T+0, repeated) | Attacker repeats the call multiple times to scale up the spoofed deposit position; cumulative spoofed-deposit total reaches ~206,809 ETH-equivalent | T10.002 scaled exploitation |
| 2022-01-27 (relayer pickup) | QBridge relayer treats the spoofed Deposit events as authentic and instructs the BSC side to mint the corresponding qXETH to the attacker's address |
T10.002 cross-chain mint without lock |
| 2022-01-27 (T+~minutes) | Attacker deposits the minted qXETH into the Qubit lending market as collateral and borrows out the entire available liquid-asset pool | T5-equivalent liquidity drain |
| 2022-01-27 (within hours) | Qubit Finance acknowledges the incident; offers $2M bug-bounty / negotiation framing and subsequently a $250k white-hat bounty | (response) |
| 2022-01-27 onward | Stolen assets bridged to Ethereum and routed through Tornado Cash | T7.001 mixer-routed laundering |
| Subsequent | No funds returned; no recovery; case settles into the public-record archive as a "0x0 deposit acceptance" canonical example | (archived) |
What defenders observed
- Pre-event (audit / unit-test layer): the absence of
msg.value > 0(or, equivalently, the absence of an explicit "this code path is never reachable when the token argument is address(0)") indeposit()is a defect that a single targeted unit test — "calldeposit()withtoken = address(0)and no ETH; expect revert" — would have caught immediately. The case is a textbook argument for adversarial unit tests at the bridge entry-points: every entrypoint should have at least one test of the form "call this function with the privileged-sentinel argument and the precondition for that path missing, and assert the call reverts." - Pre-event (formal-verification / property-based): a property of the form "for every successful
Depositevent on the Ethereum side, the contract balance increased by an equivalent amount of the corresponding asset" would have flagged the issue at audit time. This is the property that bridges across the industry have been pushed to formalise post-Qubit, although the adoption rate as of v0.1 cutoff is uneven. - At-event: the spoofed
Depositevents were on-chain-indistinguishable from authentic deposit events at the contract-emit layer; the pre-relayer detection signal was the contract-balance-vs-event-amount mismatch. Any monitoring of the form "for everyDepositevent on the Ethereum side, verify that the contract's ETH balance increased by the event'samountfield within the same transaction" would have flagged the spoofed events at the first occurrence. As of January 2022, this kind of per-bridge invariant monitoring was not industry-standard. - At-event (relayer-side): the relayer trusted the on-chain
Depositevent without performing the same balance-delta check on its side. Relayer-side independent verification is a defensive layer that several post-2022 bridge architectures have introduced; Qubit's relayer of the period did not include it. - Post-event: no recovery primitive. The Tornado Cash laundering route absorbed the proceeds within hours; the lending-market liquidity drain was complete by that point.
What this example tells contributors writing future Technique pages
- The "0x0 sentinel" failure mode is a recurring T10.002 subclass. Multiple bridges of the 2021–2022 period exposed a "native-asset is special-cased via address(0)" code path that did not robustly distinguish "the call passed address(0) and sent native asset" from "the call passed address(0) and sent nothing." Contributors writing future T10.002 examples should explicitly check for this pattern; if a bridge contract has a sentinel-value path for native assets, the contract's audit history should explicitly cite the
msg.value > 0invariant. - Mint-without-lock invariant monitoring is the runtime detection signal. As at Wormhole (
examples/2022-02-wormhole.md), the post-extraction signal that catches T10.002 in production is the per-bridge invariant "every mint event on side A pairs with a balance-increase on side B in a bounded window." For Qubit, the more local-to-the-Ethereum-side variant is "everyDepositevent corresponds to a same-transaction increase in the contract's balance of the deposited asset." Both invariants are operationally cheap; their adoption rate as of v0.1 cutoff remains uneven. - Attacker-funding via Tornado Cash is a 2022-era operational tell. Qubit, Wormhole (
examples/2022-02-wormhole.md), and Ronin (examples/2022-03-ronin-bridge.md) all involve attackers funded through Tornado Cash on the entry side. By 2022 this was a sufficiently common pattern that "attacker wallet funded via Tornado Cash" is not, by itself, an attribution signal — it is the baseline-pseudonymous starting position for the 2022 bridge cohort. Contributors should not over-read the funding pattern as an attribution data-point.
Public references
[qubitpostmortem2022](proposed) — Qubit Finance / QBridge official incident statement and post-mortem; primary operator-side source for the affected function path, the qXETH-mint geometry, and the bug-bounty / negotiation framing.[certikqubit2022](proposed) — CertiK contemporaneous forensic write-up; function-level walkthrough of thedeposit()vsdepositETH()divergence and theaddress(0)sentinel path.[halbornqubit2022](proposed) — Halborn forensic post-mortem; "0x0 deposit acceptance" framing and recommended invariant-monitoring posture.[rektqubit2022](proposed) — Rekt News write-up of the Qubit Finance / QBridge January 2022 incident; loss size, lending-market drain shape, Tornado-Cash laundering note.
Discussion
Qubit is structurally the first canonical "0x0 deposit acceptance" T10.002 case in OAK's worked-example catalogue. The defect is not novel as a class — bridges with native-asset sentinel-value handling have been a recurring 2021–2022 attack-surface — but Qubit is the case where the dollar magnitude (~$80M), the function-level clarity of the defect (deposit() vs depositETH() divergence), and the operational shape of the extraction (mint-then-borrow against the lending market) are all on the public record at high enough resolution to make it a teaching case.
The case sits one week before Wormhole (examples/2022-02-wormhole.md) on the calendar, and the two together are OAK's bridge-class pair from January–February 2022: Wormhole as the "missing guardian validation" canonical case, Qubit as the "missing native-asset deposit validation" canonical case. Both are T10.002 by operational classification and T9.004 by mechanistic classification; both involve the verification function accepting an input that the function should have treated as privileged.
A defender working on a 2022-era bridge contract who internalises both cases takes away two checklist items: (1) every authority involved in cross-chain message processing must be validated against the trusted set; (2) every native-asset-sentinel code path must validate that the corresponding native-asset transfer actually occurred. The two checklist items are independent — Wormhole satisfied (2) and failed (1); Qubit satisfied (1) and failed (2). The post-2022 bridge-architecture review process broadly internalised both, but the upgrade-path review (which is where Nomad subsequently failed — examples/2022-08-nomad-bridge.md) remains a separate axis where the industry's adoption rate is uneven.
Contributors writing future T10.002 examples with native-asset-sentinel patterns should reach for Qubit as the comparison reference: the function-level walk-through is unusually clean, and the lending-market drain shape that followed (mint synthetic collateral, then borrow out the real assets) is a recurring secondary attack pattern in bridges that are integrated with on-chain lending — a pattern that recurs at Cream, Hundred, and several other 2022–2023 cases.