BNB Chain BSC Token Hub IAVL Proof Forgery / Forged BNB Mint (2022-10-06)

  • Name: BNB Chain BSC Token Hub IAVL proof forgery / forged BNB mint
  • Date: 2022-10-06
  • Protocol: BNB Chain / BSC Token Hub
  • Chain: BNB Beacon Chain + BNB Smart Chain
  • Category: bridge verification failure / light-client proof forgery / invalid Merkle proof acceptance / unbacked native-asset mint
  • Loss: 2,000,000 BNB minted on BNB Smart Chain, worth roughly 600M at the time; public reporting generally puts assets actually bridged or swapped out before the halt at roughly 110M
  • Summary: The attacker forged BNB Beacon Chain-to-BNB Smart Chain bridge messages by abusing a bug in the IAVL range-proof verifier used by BNB Chain’s light-client precompile. In the vulnerable logic, ProofInnerNode.Hash() ignored Right whenever Left was non-empty. That let the attacker start from a real historical range proof, splice in a second malicious leaf whose value encoded a fake 1,000,000 BNB transfer, change the corresponding Right field to the malicious leaf hash, and still preserve the same computed root / appHash. Once the forged proof passed validation, BSC Token Hub treated the fake Beacon-chain package as authentic and released 1,000,000 BNB twice to the attacker.
  • Trigger / entry point:
    • BSC Token Hub relied on BNB Chain’s light-client / IAVL proof-verification path to decide whether an inbound Beacon-chain package was real.
    • The bridge accepted user-supplied Merkle proofs for cross-chain package inclusion.
    • The attacker used a genuine earlier bridge proof as a template, then converted it into a malicious range proof that still validated against the trusted root.
    • Once the proof passed, the bridge executed a forged transfer-in package as if 1,000,000 BNB had really been locked on Beacon Chain for release on BSC.
  • Root cause:
    • The IAVL proof-verification logic had a fatal asymmetry: when hashing an inner node, the implementation used Left || childHash whenever Left existed and entirely ignored Right.
    • That meant an attacker could change Right values inside a proof without changing the derived root hash, as long as the rest of the path stayed consistent.
    • Combined with range-proof recursion, this let the attacker append a malicious leaf / key-value pair and alter proof metadata so the verifier accepted membership for data that had never existed in the Beacon-chain state tree.
    • The bridge therefore failed at the authenticity boundary itself: the destination-side verifier said this package is real when the source chain had never committed it.
  • Exploit path:
    1. The attacker prepared a malicious bridge payload encoding a BNB transfer to attacker-controlled address 0x489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec.
    2. They started from a legitimate earlier BNB Beacon Chain proof, widely cited in later writeups as transaction 0x79575ff791606ef2c7d69f430d1fee1c25ef8d56275da94e6ac49c9c4cc5f433.
    3. Using that real proof as scaffolding, they inserted a second forged leaf keyed to cross-chain package sequence 17684867, set the forged leaf’s ValueHash to the hash of the malicious payload, and appended an empty InnerNodes path for the injected leaf.
    4. They then modified the proof so LeftPath[1].Right equaled the hash of the malicious leaf. Because the vulnerable inner-node hash routine ignored Right whenever Left existed, the derived root still matched the trusted Beacon-chain appHash even though the proof now vouched for attacker-invented data.
    5. The attacker submitted the forged proof through the bridge’s relayer flow, satisfying the verifier and causing BSC Token Hub to accept the fake package as an authentic Beacon-chain transfer-in.
    6. BSC Token Hub released or minted 1,000,000 BNB to the attacker, and the same general method was used again to obtain a second 1,000,000 BNB before validators halted the chain.
    7. The attacker then rushed funds into other venues and bridges; the chain halt and follow-on freezes sharply limited how much of the notional 2,000,000 BNB could actually leave the BNB ecosystem.
  • What the attacker needed to be true:
    • The bridge’s destination-side verifier needed to accept user-supplied IAVL range proofs with more than one leaf.
    • The verifier needed to preserve the buggy ProofInnerNode.Hash() behavior where Right was ignored whenever Left was present.
    • The bridge needed to treat successful proof validation as sufficient authority to execute the package, with no independent cap or sanity check on package size.
    • The attacker needed relayer access and enough time after minting to route some value into external bridges or liquid markets before the chain halted.
  • Technical anatomy worth remembering:
    • This was not a signer compromise and not a multisig failure. The attacker forged a light-client proof that the bridge itself was willing to honor.
    • The exploit path depended on range-proof semantics, not just a generic Merkle proof bug. The attacker reused a real proof, added a malicious second leaf, and exploited the mismatch between root derivation and recursive right-branch validation.
    • In the vulnerable IAVL code, ProofInnerNode.Hash() encoded (Left, childHash) when Left existed, but encoded (childHash, Right) only when Left was empty. That one branch meant some proof data was security-critical in one place and ignored in another.
    • Verichains’ early reconstruction usefully preserves the exploit’s exact asymmetry: keep the genuine first leaf and all of the left-side proof scaffolding, inject a second attacker leaf, then rewrite the Right value at the recursion point so the recursive check authenticates the forged leaf even though the top-level root calculation still commits only to the left side.
    • The BNB Chain post-incident patch therefore did two blunt but sensible things in the verifier path: reject range proofs (len(valueOp.Proof.Leaves) != 1) and reject any inner node where both Left and Right were set.
    • The incident is a strong example of bridge-verifier composition risk: the economic loss happened in BSC Token Hub, but the actual bug lived lower in the light-client proof library and precompile.

Three bridge-truth promotions this incident collapsed

  1. A real Beacon-chain proof scaffold and trusted appHash existed
    • The attacker did not invent the whole witness from nothing. They started from a genuine earlier Beacon-chain proof and a real light-client root that BSC was already willing to trust.
    • At this stage the strongest true fact was only some real bridge-proof material exists under a trusted root.
  2. That scaffold was promoted into exact transfer-package truth for sequence 17684867
    • This was the decisive failure. The bridge treated the forged range proof as if it still proved that the attacker-chosen transfer package with the malicious ValueHash actually lived under that trusted Beacon-chain root.
    • In reality the verifier had lost the stronger claim this exact executable package was committed, because the top-level root derivation and recursive right-branch validation no longer described the same message.
  3. That accepted package truth still deserved treasury-scale BNB release authority
    • Once the malformed proof returned valid, BSC Token Hub still let one package release 1,000,000 BNB, and then let the same general forgery path do it again before emergency halting became the real brake.
    • The bridge therefore collapsed proof looked valid straight into this lane may mint or release nine figures of native BNB right now, with no narrow per-package blast-radius control underneath the verifier.
  • Representative transactions and addresses:
    • Attacker recipient address used in public proof-of-concept reconstructions: 0x489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec
    • Legitimate historical proof later repurposed as exploit scaffolding: 0x79575ff791606ef2c7d69f430d1fee1c25ef8d56275da94e6ac49c9c4cc5f433
    • Public writeups also trace the exploit sequence through transaction 0xe1fe5fef26e93e6389910545099303e4fee774427d9e628d2aab80f1b53396d6
    • Post-incident verifier hardening commit: cb131fabe5fb9570180e7030a293a984f17c2446
  • Response / mitigation:
    • BNB Chain validators coordinated an emergency halt of BNB Smart Chain to stop further asset movement.
    • The light-client verifier was patched to suspend range proofs and reject inner nodes that simultaneously set Left and Right.
    • BNB Chain later pushed broader cross-chain hardening, including additional governance and large-transfer controls, after the immediate containment phase.

Pattern tags

  • proof-to-message-binding-failure
  • bridge-verification-failure
  • Authenticated root stayed real while attacker-chosen transfer package escaped the proof object’s real membership boundary
  • One low-level proof-library asymmetry inherited full bridge-release authority upstream of any route-local throttle

Why the exploit worked

  • BNB Chain’s bridge trusted a proof object that no longer committed to the same message it was later willing to execute. The Beacon-chain appHash stayed genuine, but the vulnerable IAVL verifier stopped making this transfer package is inside that committed state true once Right could change without changing the derived inner-node hash.
  • The exploit path therefore was not bridge logic ignored verification; it was verification itself started authenticating a wider and attacker-editable proof story than the bridge believed. By the time BSC Token Hub saw proof valid, the authenticity boundary had already failed below it.
  • The range-proof shape mattered because it let the attacker preserve real proof scaffolding, inject a malicious second leaf, and still inherit the authority of a trusted historical root. This was stronger than a generic bad-leaf check bug: the bridge’s own proof composition let one real root bless a different message.
  • BSC Token Hub also lacked an internal blast-radius brake between one forged transfer package passes verification and 1,000,000 BNB release. Once the proof library lied, the route still exposed treasury-scale mint / release authority twice before social coordination and a chain halt became the real defense.
  • The key preserved lesson is therefore not only audit the light client. It is the bridge's executable message must stay cryptographically glued to the exact authenticated proof object all the way down the stack, and one proof-family bug should not immediately command uncapped custody.

Detection and audit pivots

  • Enumerate every proof shape the bridge accepts for message inclusion. If only single-leaf membership is economically meaningful, ask why range proofs, multi-leaf paths, or helper proof families still retain equal release authority.
  • For each proof helper, ask what exact fields contribute to the committed root in every branch. If one child hash, sibling pointer, or path field is security-critical in one recursion branch but ignored in another, treat that asymmetry as a custody bug, not a serialization quirk.
  • Reconstruct the executed bridge message from the authenticated proof object and compare it byte-for-byte with the package actually handed to release logic. The useful question is not only did the root verify? but did the verified object still uniquely imply this exact transfer package?
  • Review low-level proof libraries and precompiles as part of the bridge trust boundary. If the economic system spends directly against their boolean output, their field semantics, recursion rules, and malformed-proof rejection policy deserve the same scrutiny as signer-threshold or mint-authority code.
  • Ask what happens after one proof-family failure. If a single accepted forged package can mint or release treasury-scale inventory before a route-local cap, delay, or per-epoch limit bites, the bridge still relies too heavily on verifier perfection.

Failure-prevention checklist

  • Narrow bridge-proof authority to the exact witness shape the route actually needs. If Token Hub only needs one transfer package membership proof, single-leaf membership should be the only accepted form; range proofs, extra leaves, and richer helper families should not keep equal mint or release authority just because the underlying library can parse them.
  • Make the verifier consume the exact claimed transfer package or package hash, not merely a root plus a proof blob. The bridge should prove sequence 17684867 with this exact payload existed under this Beacon-chain root, then hand that same canonical object to execution.
  • Treat every proof-field branch as treasury-critical. Inner-node hashing, left/right occupancy rules, subtree size, path orientation, and malformed-leaf handling all need fail-closed tests so no sibling field can become economically meaningful in one recursion path and silently ignored in another.
  • Review light-client precompiles and proof libraries as custody migrations whenever they change. A patch like BNB Chain’s post-incident range-proof suspension and verifier hardening is not middleware maintenance; it is a live redefinition of what the bridge will treat as authentic source-chain truth.
  • Put route-local blast-radius controls under verifier success: per-package caps, rolling per-epoch release ceilings, delayed handling for unusually large bridge transfers, and fast bridge-lane pauses that do not require a full chain halt to stop one counterfeit package stream.
  • Add independent replay and shadow-verification checks for high-value native bridges. If one implementation says a proof is valid, a second implementation or offline monitor should be able to reconstruct the same package membership and detect impossible package values before treasury-scale release.
  • Alert on bridge messages whose amount, sequence progression, or repetition is grossly outside normal route behavior. Two 1,000,000 BNB transfer-ins should have looked like a bridge emergency even before secondary exits began.
  • Keep bridge liabilities continuously reconcilable against source-side locked value so a forged proof path cannot sit undetected as merely one successful transfer instead of unbacked native asset now exists on BSC.

Audit prompts

  • Can any real historical Token Hub proof be replayed with a modified leaf, sibling hash, or path field while still preserving the same trusted appHash?
  • What exact bytestring does BSC Token Hub mean to authenticate: package sequence, channel, recipient, amount, and payload? Where is that exact object bound before proof verification returns valid?
  • Does the route accept proof forms whose expressive power exceeds the intended claim, such as range proofs or multi-leaf witnesses for what is economically only one-message membership?
  • In every proof-helper branch, which fields actually influence the derived root and which only look security-relevant? If Left is non-empty, can Right or another sibling field still change message meaning without changing the committed root?
  • If the proof layer lies once, what exact onchain control limits cumulative loss before operators coordinate a halt: per-package ceiling, per-epoch cap, delayed settlement lane, or asset-local pause?
  • Are there shadow monitors that independently rebuild the claimed Beacon-chain package from the accepted proof and compare it to the executed BSC transfer-in package?
  • What abnormal package-size, repetition, or sequence-jump alerts would have fired on the first forged 1,000,000 BNB message, and do those alerts have authority to cut off the affected bridge lane quickly?
  • Are proof-library upgrades, precompile changes, and bridge-verifier patches tested against a corpus of malformed one-leaf, two-leaf, and branch-asymmetry cases that try to preserve a real root while mutating the executed package?

Lessons that should already be standard

  • Consensus-proof helper code becomes treasury code the moment a bridge spends directly against its answer. Low-level branch asymmetry in a proof library is not a theoretical correctness bug once it can mint or release nine figures of native assets.
  • Trusted root stayed real is not enough. A bridge needs the stronger invariant that the exact executable package remained cryptographically bound to that root all the way through verification and dispatch.
  • If a route only needs one-message membership, richer proof shapes are usually just extra attack surface. Minimal witness shape is a custody control, not an optimization preference.
  • One-sided or branch-dependent field consumption should be treated as a release-authority defect immediately. If one sibling hash can change meaning without changing the root in some branch, the bridge already has counterfeit-authenticity risk.
  • Emergency validator coordination and a full chain halt are not healthy bounded-release design. They are last-resort social containment after the endogenous controls already failed.
  • High-value native bridges should assume proof verifiers can be wrong and place blast-radius controls underneath them. Without caps, queues, or fast lane-local brakes, one forged proof path becomes one treasury-scale mint path.

Why this incident matters

  • It is one of the clearest forged proof becomes forged mint bridge failures in a major production network.
  • The exploit shows how custody risk can hide in low-level proof libraries that application-layer bridge contracts treat as unquestionable truth.
  • It also shows why only one branch of this proof field is used here is not an implementation detail. In a bridge verifier, that kind of asymmetry is the trust boundary.
  • It is also a strong bounded-release case study: the same proof forgery path minted 1,000,000 BNB twice before validators halted the chain, showing how little separation existed between false light-client truth accepted and treasury-scale release authority; the eventual reduction in realized outflow came from emergency halting, not from a narrow endogenous blast-radius budget.

Internal linkages

Control surface