OAK — OnChain Attack Knowledge

Worked example · 2026-05

MAP Protocol — Butter Bridge abi.encodePacked hash-collision + retry-message replay — 2026-05-20

Loss
~$110K realised (the attacker liquidated freshly minted tokens for ~52.2 ETH), against a notional supply event of 1,000,000,000,000,000 MAPO (one quadrillion) minted on Ethereum — roughly 4.8 million times the legitimate circulating supply of ~208M MAPO. The realised cash-out is small because the bridge-minted tokens had no real backing and the market priced them to near zero on discovery: MAPO fell ~96% (≈$0.003 → ≈$0.0001) within hours. The gap between the ~$110K the attacker walked away with and the effectively unbounded mint is the structural signature of a supply-side bridge-mint exploit — the constraint on extraction is market depth at exit, not the size of the unauthorised mint.
OAK Techniques observed
OAK-T10.002 (Message-Verification Bypass — the canonical anchor. The bridge's retry-message verification computed its integrity hash with keccak256(abi.encodePacked(...)) over multiple dynamic fields; non-injective packing let two distinct message layouts collide to the same hash, so a forged retry message passed a verification step that had been designed around a legitimate one. See techniques/T10.002-message-verification-bypass.md). OAK-T10.003 (Cross-Chain Replay — the attack reused a genuine, oracle-and-multisig-signed MAP→ETH bridge message and re-presented it through the retry path; the replay was the carrier for the collision). OAK-T9.004 (cross-referenced — the underlying defect is a contract-correctness flaw: an ambiguous hash-encoding choice in the verification predicate, the same way Wormhole is cross-referenced under both T10.002 and T9.004). OAK-T10 (Bridge / Cross-Chain parent).
Attribution
pseudonymous attacker. The exploit required deploying a contract at a precomputed (CREATE2) address to interpose on the retry function, then driving the mint — an execution pattern consistent with a prepared, single-operator attack rather than an opportunistic copy. No public attribution to a named operator group. Blockaid (@blockaid_) identified and published the root-cause analysis; MAP Protocol confirmed the incident and paused bridge operations.
Key teaching point
Butter Bridge is the 2026 worked example of the abi.encodePacked hash-collision sub-class of T10.002 — a verification-bypass that does not defeat any signature or proof, but exploits the non-injectivity of keccak256(abi.encodePacked(a, b, …)) when more than one argument is a dynamic type. abi.encodePacked concatenates its arguments without length prefixes or padding, so encodePacked("a", "bc") and encodePacked("ab", "c") produce identical byte strings and therefore identical hashes. A bridge that derives a message's identity or integrity tag from a packed hash of variable-length fields can be handed a different message that hashes to the same tag — and if the tag is what the verification path trusts, the forged message is accepted. The detection-signal locus is the audit / static-analysis layer: abi.encodePacked with two or more dynamic arguments feeding a security-relevant hash is a known Solidity anti-pattern (flagged by Slither and the Solidity docs themselves) and should be a hard finding in any bridge audit.

Summary

MAP Protocol operates Butter, a cross-chain bridge connecting MAP Relay Chain, Ethereum, BNB Chain and other networks. Cross-chain transfers are authorised by an oracle-and-multisig-signed message; the bridge supports a retry path so that a transfer whose destination-side execution failed can be re-submitted and completed.

On 2026-05-20, an attacker abused the retry path to mint one quadrillion MAPO on Ethereum with no corresponding lock or burn on the source chain. Per Blockaid's root-cause analysis, the sequence was:

  1. The attacker initiated a legitimate MAP→ETH bridge message, signed through the bridge's oracle and multisig validation. This message was genuine — no signature was forged and no key was compromised.
  2. The attacker deployed a new contract at a precomputed address (CREATE2, so the address could be known in advance and referenced by the crafted message) to interpose on the bridge's retry function.
  3. The attacker re-presented the transfer through the retry path with a message whose fields were re-arranged so that keccak256(abi.encodePacked(...)) of the forged layout collided with the hash of the legitimate layout the bridge had already accepted. Because the bridge's retry verification trusted that packed hash as the message's integrity tag, it processed the forged message — minting 1,000,000,000,000,000 MAPO to the attacker.

The attacker swapped a portion of the minted supply into 52.2 ETH ($110K) before the market collapsed the price. MAP Protocol paused all bridge operations, and announced a recovery/migration plan: deploy a new token contract and take a snapshot to separate authentic balances from attacker-minted tokens.

Blockaid is explicit about what the incident is: a weakness in the retry message verification process, specifically the use of keccak256(abi.encodePacked(...)) over packed dynamic fields, combined with message replay and address manipulation (the precomputed contract) to bypass the validation system. It is not a signature forgery and not a validator-key compromise — the signed message the attack rode on was genuine.

Why this is structurally significant

T10.002 (Message-Verification Bypass) groups failures where a bridge releases or mints value against a message it should have rejected. Butter Bridge adds a sub-class distinct from the three already anchored in the corpus:

  1. Trusted-root-initialisation sub-class (Nomad, 2022-08). A contract upgrade set the trusted root to 0x00; the verifier accepted every message. The flaw was in the initialisation of verification state.
  2. Signature/account-validation sub-class (Wormhole, 2022-02). A deprecated signature path did not validate the guardian-account address, allowing a forged VAA. The flaw was in who the proof was checked against.
  3. Economic-value-binding sub-class (Verus, 2026-05). Every cryptographic check passed; no check bound the payout to source-side economic totals. The flaw was a missing conservation invariant (see examples/2026-05-verus-ethereum-bridge-source-amount-validation.md).
  4. Hash-encoding-ambiguity sub-class (Butter, this case). The verification hash itself is ambiguous: abi.encodePacked over multiple dynamic fields is not injective, so a forged message can be constructed that hashes identically to a legitimate one. The flaw is in the choice of hashing primitive in the verification predicate — the tag the verifier trusts does not uniquely identify the message it tags.

The four sub-classes have different detection loci. Nomad's lives at the post-upgrade smoke-test layer; Wormhole's at the pre-deployment audit of the signature path; Verus's at the invariant-specification layer; Butter's lives at the static-analysis / code-review layer, because abi.encodePacked with ≥2 dynamic arguments feeding a security-relevant hash is mechanically detectable before deployment. This makes Butter the most preventable of the four: the anti-pattern is documented, lint-detectable, and has been the subject of public Solidity guidance for years. The teaching value is precisely that a known, tool-detectable footgun reached a production bridge's verification path.

The case also pairs the replay and collision primitives: the collision alone is inert without a legitimate signed message to collide against, and the replay alone would be caught by a nonce/used-message check. The attacker needed both — a genuine signed message (the replay carrier) and an ambiguous integrity tag (the collision) — plus a precomputed contract address to make the forged layout resolve correctly. Contributors should record this as a composed T10.002 + T10.003 case rather than a single-primitive one.

Timeline (UTC)

When Event OAK ref
Pre-2026-05-20 Butter Bridge operates legitimately; cross-chain transfers authorised by oracle+multisig-signed messages; retry path re-submits failed transfers; retry verification derives a message integrity tag via keccak256(abi.encodePacked(...)) over dynamic fields (standing T10.002 surface)
2026-05-20 Attacker initiates a legitimate MAP→ETH bridge message (validly signed by oracle + multisig) (genuine message — replay carrier)
2026-05-20 Attacker deploys a contract at a precomputed (CREATE2) address to interpose on the retry function T10.002 setup (address manipulation)
2026-05-20 Attacker re-presents the transfer via retry with a re-arranged field layout whose abi.encodePacked hash collides with the accepted message; bridge processes the forged retry and mints 1,000,000,000,000,000 MAPO on Ethereum T10.002 + T10.003 execution (hash-collision + replay)
2026-05-20 Attacker swaps part of the minted supply into 52.2 ETH ($110K); MAPO price craters ~96% (≈$0.003 → ≈$0.0001) T5 outflow
2026-05-20 Blockaid publishes root-cause analysis (retry-verification abi.encodePacked weakness + replay + address manipulation) (third-party detection / forensic surface)
Post-2026-05-20 MAP Protocol pauses all bridge operations; announces snapshot + new-token-contract migration to separate authentic balances from attacker-minted MAPO (operator response / recovery)

What defenders observed

  • Pre-event (code / static-analysis layer): the retry-verification path computed a message integrity tag with keccak256(abi.encodePacked(...)) over two or more dynamic fields. This is a mechanically detectable anti-pattern — Slither's encode-packed-collision detector and the Solidity documentation both warn that packing multiple dynamic types is not injective. A bridge audit that ran standard tooling against the verification path should have surfaced it. Defender lesson: treat any abi.encodePacked feeding a security-relevant hash as a hard finding; use abi.encode (length-prefixed, injective) or fixed-width fields for any value that participates in message identity or integrity.
  • At-event (on-chain signal): a single retry transaction produced a mint of one quadrillion MAPO — ~4.8M× the entire legitimate supply. A supply-monitoring indicator (mint event whose magnitude is implausible relative to historical supply, or a bridge mint with no matching source-chain lock/burn) would have flagged this in real time. The mint-vs-lock conservation check (the Verus lesson) would also have caught it independently.
  • At-event (address signal): the attack relied on a contract deployed at a precomputed address to interpose on the retry function. A fresh contract whose address is referenced by a bridge retry message, deployed shortly before the retry call, is an elevated-risk pattern.
  • Post-event: the drained value was small (~52.2 ETH) relative to the mint because the unbacked tokens could not be exited at scale; the price collapsed on discovery. The recovery path (snapshot + new token contract) is the standard remediation for an unbounded-mint event where the legitimate/illegitimate balance split is computable from chain history.

What this example tells contributors writing future Technique pages

  • T10.002 needs a documented hash-encoding-ambiguity sub-class. It is mechanically distinct from initialisation (Nomad), account-validation (Wormhole), and economic-binding (Verus) failures: here the integrity tag itself is ambiguous. Record which sub-class a new T10.002 case belongs to — the audit guidance for "use abi.encode, not abi.encodePacked" is specific to this one.
  • A known, lint-detectable footgun in a verification path is the most instructive kind of finding. Unlike a missing semantic invariant (Verus), abi.encodePacked collision is caught by off-the-shelf static analysis. The lesson is process, not insight: the tool exists; it was not run, or its finding was not actioned, on the verification path.
  • Replay and collision compose. Record Butter as T10.002 (collision) + T10.003 (replay): the genuine signed message was the replay carrier, and a used-message/nonce guard on the retry path would have broken the chain even with the collision present. Bridges should treat "a message may be retried" as a replay surface and gate it with a consumed-message set keyed on an injective identifier.
  • Mint magnitude is a first-class signal for supply-side bridge exploits. When the constraint on extraction is exit liquidity rather than the mint size, the realised loss understates the event. Record both the realised figure (~$110K) and the notional supply event (1e15 MAPO) — the same notional-vs-realised discipline OAK applies to Hyperbridge.

Public references

Discussion

Butter Bridge completes a four-way reference set for T10.002 alongside Nomad (trusted-root initialisation), Wormhole (signature/account validation) and Verus (economic-value binding). It is the only one of the four whose root cause is a named, tool-detectable Solidity anti-pattern: keccak256(abi.encodePacked(...)) over multiple dynamic fields. Where Verus teaches that auditors must articulate semantic invariants no single proof encodes, Butter teaches the opposite-feeling but equally important lesson — that a mechanical, already-documented footgun can still reach a production bridge's verification path, and that running standard static analysis on the verification code is not optional.

The case pairs naturally with Verus (examples/2026-05-verus-ethereum-bridge-source-amount-validation.md) as the two May-2026 bridge-mint/reserve failures where the cryptographic signing machinery worked as designed and the attacker's leverage was a gap in what the verified message was bound to (Verus) or how the verified message was identified (Butter). Both reinforce the corpus's "binding gaps" family: proof-to-message (Hyperbridge), message-to-source-backing (Verus), message-to-origin (CrossCurve, in examples/2026-q1-q2-crosschain-bridge-otc-cohort.md), and now message-to-integrity-tag (Butter). Contributors building out the T10 bridge cohort should treat these as a coherent family of binding failures distinct from key-compromise (T10.001) and pure replay (T10.003) — even though, as Butter shows, replay is frequently the carrier that makes a binding gap exploitable.

Techniques demonstrated (4)