{
 "slug": "bybit-wazirx-multisig-hack-payload-mismatch-postmortem",
 "topic_id": "TOPIC-073",
 "cluster": "Digital Asset Custody",
 "tier": "Tier A",
 "title": "The payload flip: signing what you did not see",
 "summary": "The largest digital asset thefts did not involve stolen keys. Signers held their hardware, reviewed a transaction in a web interface, and signed a different transaction.",
 "lede": "Every signer did what the procedure asked. They held the device, they read the screen, they approved. The screen and the payload were not the same object, and nothing in the process was designed to notice.",
 "date": "2026-05-30",
 "category": "Crypto",
 "author_id": "nadia-ferreira-strand",
 "tags": [
  "multisig",
  "hardware wallet",
  "WYSIWYS",
  "payload mismatch",
  "custody",
  "blind signing"
 ],
 "image_title": "The Payload Flip Multisig",
 "schema": "Article",
 "key_takeaways": [
  "The private keys were never compromised. The attack targeted the gap between the interface and the signed bytes.",
  "A single flag inside a transaction can change its meaning entirely while the displayed summary remains accurate-looking.",
  "What-you-see-is-what-you-sign requires the rendering to be derived from the exact bytes being signed, verified independently of the interface."
 ],
 "body": [
  {
   "type": "h2",
   "text": "The shared structure"
  },
  {
   "type": "diagram",
   "kind": "chain",
   "alt": "The interface shows a routine transfer; the hardware wallet signs a delegatecall that rewrites the wallet's owners",
   "caption": "What the signer saw and what the signer signed were two different objects. Nothing in the flow compared them.",
   "nodes": [
    {
     "label": "Interface",
     "sub": "shows transfer",
     "note": "compromised"
    },
    {
     "label": "Signer reviews",
     "sub": "reads the screen",
     "note": "human"
    },
    {
     "label": "Hardware wallet",
     "sub": "shows a hash",
     "note": "cannot parse"
    },
    {
     "label": "Payload executes",
     "sub": "delegatecall",
     "note": "owners rewritten",
     "bad": true
    }
   ]
  },
  {
   "type": "p",
   "html": "Across several large incidents the pattern is the same, whatever the specific platform."
  },
  {
   "type": "ol",
   "items": [
    "Signers use hardware devices. Keys are never exported and are not stolen.",
    "Transaction construction and review happen in a web interface.",
    "The interface presents a human-readable summary of what the transaction does.",
    "The bytes sent to the hardware device differ from what the summary described.",
    "The hardware device displays what it can — often a hash or a truncated representation — which does not contradict the summary.",
    "Signers approve. The transaction executes with its actual, different effect."
   ]
  },
  {
   "type": "p",
   "html": "Step five is where the defence was supposed to be and where it does not function. A device with a small screen and no contract-parsing capability cannot render a complex transaction meaningfully."
  },
  {
   "type": "h2",
   "text": "How a flag changes everything"
  },
  {
   "type": "p",
   "html": "In the Bybit case, a single operation parameter inside the payload was altered. The semantic difference between its two values is enormous."
  },
  {
   "type": "table",
   "head": [
    "Operation value",
    "Semantics",
    "Practical effect"
   ],
   "rows": [
    [
     "0 — call",
     "Execute a call to the target contract",
     "The target's code runs in the target's context"
    ],
    [
     "1 — delegatecall",
     "Execute the target's code in the caller's context",
     "<strong style=\"font-weight:600\">The target's code runs with the multisig's own storage and authority</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The second row means the transaction can rewrite the multisig's own state — including who its owners are. One integer field, invisible in a summary that shows destination and value."
  },
  {
   "type": "p",
   "html": "This is not an obscure edge case. It is a documented parameter with a documented meaning, and the interface simply did not surface it because summaries are written around what users usually care about."
  },
  {
   "type": "h2",
   "text": "Why hardware devices cannot save this"
  },
  {
   "type": "p",
   "html": "The security model of a hardware wallet is key isolation: the private key never leaves the secure element. It does that job well and it is the reason keys were not stolen in any of these incidents."
  },
  {
   "type": "p",
   "html": "What it cannot do is semantic verification. Parsing arbitrary contract calls, resolving addresses to known entities and rendering the consequences requires code, data and screen area that a constrained device does not have."
  },
  {
   "type": "p",
   "html": "So it displays what it can and, when it cannot parse, warns that the user is signing blind. That warning is accurate and users click past it because the alternative is being unable to transact."
  },
  {
   "type": "h2",
   "text": "What WYSIWYS actually requires"
  },
  {
   "type": "p",
   "html": "Three properties, and most implementations provide the first only."
  },
  {
   "type": "table",
   "head": [
    "Property",
    "Common state"
   ],
   "rows": [
    [
     "The user sees something",
     "Yes"
    ],
    [
     "What they see is derived from the exact bytes to be signed",
     "<strong style=\"font-weight:600\">Often not — derived from the interface's own model</strong>"
    ],
    [
     "The derivation is verifiable independently of the interface",
     "<strong style=\"font-weight:600\">Rarely</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The second row is the whole vulnerability. If the summary comes from the application's intent rather than from the payload, a compromised or manipulated application produces a correct-looking summary for a different transaction."
  },
  {
   "type": "h2",
   "text": "A design that closes it"
  },
  {
   "type": "code",
   "text": "# The statement is derived FROM the payload, not alongside it.\n\npayload   = build_transaction(...)          # the exact bytes\nstatement = render_from_bytes(payload)       # decoded, not described\n\n# Every consequential field is present and rendered:\n#   to, value, data selector, decoded arguments,\n#   operation flag, nonce, chain id, gas parameters\n\ndigest = sha256(JCS(statement))\n\n# The signer's credential signs the digest.\n# Before broadcast, an independent verifier recomputes\n# render_from_bytes(payload) and compares.\n# Mismatch → refuse. Not warn. Refuse."
  },
  {
   "type": "p",
   "html": "The independence of the verifier is the load-bearing part. If the same component that built the payload also renders and verifies it, a compromise of that component defeats all three steps at once."
  },
  {
   "type": "h2",
   "text": "What custody operations can do now"
  },
  {
   "type": "ol",
   "items": [
    "Decode every transaction independently before signing, using tooling that does not share code with the interface. A second implementation catches what one implementation misses.",
    "Require the operation flag, target, value, decoded arguments and chain id to be displayed explicitly, every time, with no summarisation.",
    "Compare the payload hash on the hardware device against a hash computed independently. Slow, tedious, and it catches this exact attack.",
    "Use simulation against a forked state to observe what the transaction actually does, and present the resulting state diff to signers.",
    "Treat any transaction the tooling cannot fully decode as a refusal rather than a warning."
   ]
  },
  {
   "type": "p",
   "html": "The fifth is the cultural change and it is the hardest. Every one of these incidents included a step where someone proceeded past an indication that full verification was not possible."
  },
  {
   "type": "h2",
   "text": "The uncomfortable summary"
  },
  {
   "type": "p",
   "html": "The cryptography worked. The hardware worked. The multisignature scheme worked — the required number of authorised signers did sign."
  },
  {
   "type": "p",
   "html": "What failed was the assumption that a signer approving a transaction had seen that transaction. Every control in the stack was built on that assumption, and none of them verified it."
  },
  {
   "type": "h2",
   "text": "What the Bybit incident actually showed"
  },
  {
   "type": "p",
   "html": "On 21 February 2025 roughly <strong style=\"font-weight:600\">401,347 ETH</strong>, valued at about <strong style=\"font-weight:600\">$1.46 billion</strong>, left a Bybit cold wallet in a single transaction. It remains the largest cryptocurrency theft on record, and investigators attributed it to the North Korean Lazarus Group."
  },
  {
   "type": "p",
   "html": "The mechanism is the part worth studying. A macOS workstation belonging to a Safe{Wallet} developer was compromised earlier in February. That access let the attackers alter what the Safe{Wallet} interface displayed to Bybit's signers. Three authorised signers reviewed what looked like a routine internal transfer and approved it. The transaction that executed changed the wallet's control logic instead."
  },
  {
   "type": "table",
   "caption": "Bybit, 21 February 2025 — what held and what did not",
   "head": [
    "Control",
    "Held?",
    "Why"
   ],
   "rows": [
    [
     "Hardware key isolation",
     "<strong style=\"font-weight:600\">Yes</strong>",
     "No private key was ever extracted"
    ],
    [
     "Multisignature quorum",
     "<strong style=\"font-weight:600\">Yes</strong>",
     "The required number of authorised signers did sign"
    ],
    [
     "Signer review",
     "<strong style=\"font-weight:600\">No</strong>",
     "They reviewed a compromised interface"
    ],
    [
     "Device display",
     "<strong style=\"font-weight:600\">No</strong>",
     "Too constrained to render the call meaningfully"
    ],
    [
     "Independent payload check",
     "<strong style=\"font-weight:600\">Absent</strong>",
     "Nothing recomputed the effect from the bytes"
    ]
   ]
  },
  {
   "type": "p",
   "html": "WazirX, in July 2024, lost roughly $235 million to a structurally identical failure: signers on a multisignature wallet approved through an interface whose display did not match the payload their devices signed. Two incidents, two vendors, one missing control."
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“This was a supply chain compromise, not a signing problem.”</strong> Both are true. The initial access was a developer workstation; the reason that access was worth $1.46 billion is that nothing downstream verified the payload independently. A second decoding path would have caught it even with the interface fully compromised."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“A better hardware wallet display would have fixed it.”</strong> Only partly. A device that could render the operation flag would have helped here. It still cannot resolve addresses to entities, decode nested calls, or show a state diff. The rendering has to happen somewhere with room for it, and be verified somewhere that did not build the transaction."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“We use MPC, not multisig.”</strong> The distinction does not apply here. MPC protects key material; it signs whatever message the initiating system supplies. See <a href=\"multi-party-computation-mpc-vs-seen-signed-receipts.html\">why splitting the key does not decide whether to sign</a>."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">What independent verification does not fix:</strong> a signer who is coerced, an allow-list that already contains a hostile address, or a policy engine configured to permit the transfer. It closes the display gap and nothing else."
  }
 ],
 "faq": [
  {
   "q": "Were private keys stolen?",
   "a": "No. The keys stayed in the secure elements. The attack exploited the gap between what the interface displayed and what the payload contained."
  },
  {
   "q": "Why can't a hardware wallet parse the transaction?",
   "a": "Its security model is key isolation, which it does well. Parsing arbitrary contract calls and rendering their consequences requires code, data and screen area it does not have."
  },
  {
   "q": "What is the operation flag issue?",
   "a": "One integer distinguishes calling a contract from executing its code in your own context. The second lets the transaction rewrite the multisig's own state, including its owners."
  },
  {
   "q": "What is the single most important change?",
   "a": "Render the statement from the exact bytes to be signed, and verify that derivation with tooling independent of the interface that built the payload."
  },
  {
   "q": "How much was taken in the Bybit hack?",
   "a": "About 401,347 ETH, roughly $1.46 billion at the time, in a single transaction on 21 February 2025. It is the largest cryptocurrency theft recorded."
  },
  {
   "q": "Were Bybit's private keys stolen?",
   "a": "No. The keys stayed in their hardware. The attackers changed what the signing interface displayed, so authorised signers approved a transaction that differed from the one they reviewed."
  },
  {
   "q": "How did the attackers reach the interface?",
   "a": "Investigators traced the initial access to a compromised developer workstation at Safe{Wallet} earlier in February 2025, which allowed the displayed transaction to be manipulated."
  },
  {
   "q": "Would a Ledger or Trezor have prevented this?",
   "a": "No. Those devices protect the key and physically confirm signing. They cannot parse a complex contract call, so they show a hash the signer cannot meaningfully check."
  }
 ],
 "sources": [
  {
   "t": "Safe smart account documentation",
   "u": "https://docs.safe.global/"
  },
  {
   "t": "Solidity documentation — delegatecall and low-level calls",
   "u": "https://docs.soliditylang.org/en/latest/units-and-global-variables.html#members-of-address-types"
  },
  {
   "t": "Ledger — blind signing and transaction clarity documentation",
   "u": "https://support.ledger.com/article/4405481324433-zd"
  },
  {
   "t": "NCC Group — in-depth technical analysis of the Bybit hack",
   "u": "https://www.nccgroup.com/research/in-depth-technical-analysis-of-the-bybit-hack/"
  },
  {
   "t": "Sygnia — investigation into the Bybit hack",
   "u": "https://www.sygnia.co/blog/sygnia-investigation-bybit-hack/"
  },
  {
   "t": "Bybit — security incident timeline and FAQs",
   "u": "https://learn.bybit.com/en/this-week-in-bybit/bybit-security-incident-timeline"
  },
  {
   "t": "MITRE ATT&CK — Lazarus Group (G0032)",
   "u": "https://attack.mitre.org/groups/G0032/"
  }
 ],
 "related": [
  {
   "slug": "hardware-wallet-blind-signing-ledger-trezor-screens-cant",
   "title": "Why hardware wallet screens cannot protect you",
   "category": "Crypto"
  },
  {
   "slug": "erc-7730-vs-manav-statement-v1-descriptor-registries",
   "title": "Descriptor registries as an attack surface",
   "category": "Comparison"
  },
  {
   "slug": "multi-party-computation-mpc-vs-seen-signed-receipts",
   "title": "MPC versus seen-and-signed",
   "category": "Comparison"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/The%20Payload%20Flip%20Multisig/1200_630/blog.jpg",
 "wordcount": 1414,
 "url": "/blog/bybit-wazirx-multisig-hack-payload-mismatch-postmortem.html",
 "reading_time": "6 min read",
 "meta_description": "The largest digital asset thefts did not involve stolen keys. Signers held their hardware, reviewed a web interface, and signed something else.",
 "hub": {
  "slug": "topics/digital-asset-custody",
  "title": "Digital asset custody"
 },
 "answer": "Because the Safe{Wallet} interface they reviewed and the bytes their hardware wallets signed were different objects. Attackers who had compromised a Safe{Wallet} developer workstation altered what the signers saw. The private keys were never stolen: three authorised signers approved, and the signature verified perfectly.",
 "answer_q": "Why did the Bybit signers approve a transaction they never intended?",
 "entities": [
  {
   "name": "Bybit",
   "type": "Organization",
   "url": "https://www.bybit.com/",
   "primary": true
  },
  {
   "name": "Safe{Wallet}",
   "type": "SoftwareApplication",
   "url": "https://safe.global/"
  },
  {
   "name": "Lazarus Group",
   "type": "Organization",
   "url": "https://attack.mitre.org/groups/G0032/"
  }
 ],
 "glossary": [
  {
   "term": "delegatecall",
   "def": "An EVM call that runs the target contract's code in the <em>caller's</em> storage context. A multisignature wallet executing a delegatecall can have its own owner list rewritten by the code it calls."
  },
  {
   "term": "Blind signing",
   "def": "Approving a transaction the signing device cannot parse or display, so the human confirms a hash rather than an effect."
  },
  {
   "term": "WYSIWYS",
   "def": "What You See Is What You Sign: the property that the rendering a human approved is derived from the exact bytes that get signed, and is verifiable independently of the application that produced them."
  },
  {
   "term": "Operation flag",
   "def": "The field in a Safe transaction that selects <code>call</code> (0) or <code>delegatecall</code> (1). One integer, and the difference between moving funds and handing over the wallet."
  }
 ],
 "checklist": {
  "title": "Auditing your own signing path",
  "id": "audit",
  "desc": "Five checks a custody team can run this week against their multisignature or MPC signing flow.",
  "steps": [
   {
    "name": "Decode independently.",
    "text": "Decode every transaction with tooling that shares no code with the interface that built it, and compare field by field."
   },
   {
    "name": "Render every consequential field.",
    "text": "Target, value, calldata selector, decoded arguments, the operation flag, nonce and chain id — with no summarisation."
   },
   {
    "name": "Compare the hash out of band.",
    "text": "Read the payload hash off the hardware device and check it against a hash computed on a separate machine."
   },
   {
    "name": "Simulate against forked state.",
    "text": "Show signers the resulting balance and ownership diff rather than the calldata."
   },
   {
    "name": "Treat undecodable as refusal.",
    "text": "If the tooling cannot fully decode the transaction, the answer is no — not a warning to acknowledge."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "Manav renders the statement from the exact bytes that will be signed, hashes it with RFC 8785 canonical JSON, and has the executing service recompute that digest before it acts. A payload that changed after approval produces a mismatch and the transaction stops.",
  "href": "../docs.html",
  "label": "How statement binding works"
 }
}