Incident analysis

Anatomy of the Bybit hack: why protecting the key wasn’t enough

9 min read

On February 21, 2025, Bybit lost approximately $1.5 billion from a cold Safe wallet — at the time, the largest single theft in crypto history. By April, the forensic record was settled enough to draw the architectural lesson, and it is the most expensive lesson in the history of onchain custody.

The signers signed. The hardware wallets did exactly what hardware wallets are supposed to do. The multisig threshold did exactly what a multisig threshold is supposed to do. The Safe smart contracts did exactly what they were specified to do. The keys were never compromised.

And $1.5B left the wallet.

The custody industry spent fifteen years optimizing for one threat — adversaries trying to steal keys — and built increasingly sophisticated solutions to that problem: hardware security modules, multi-party computation, secure enclaves, hardware wallets, multi-signature schemes. By the morning of the Bybit incident, every one of those technologies was considered table stakes for institutional custody.

And none of them would have prevented the actual attack.

What happened

The forensic picture became clear over the four weeks following the incident, anchored by post-mortems from Safe{Wallet} and Mandiant, NCC Group, Sygnia, and the FBI’s formal attribution. The compressed version:

  1. Attackers compromised the build pipeline for the Safe{Wallet} frontend by hijacking AWS session tokens from a developer macOS workstation — bypassing MFA — and using the credentials to push malicious JavaScript into the assets served from app.safe.global. Sygnia confirmed the JS resources were last modified on February 19, two days before the attack.
  2. On February 21, Bybit signers opened the Safe interface to approve what looked like a routine internal transfer. The interface showed them the routine transfer they expected. The actual transaction payload was a contract-upgrade call replacing the Safe’s implementation with one controlled by the attacker.
  3. The signers used their hardware wallets to confirm. NCC Group’s teardown identified the technical primitive that made this plausible: an EIP-712 signing payload that did not bind the delegate-call branch of the operation in a way the signers could verify on-device. “EIP-712 is insufficient for nested operations,” they write.
  4. The malicious implementation drained the wallet to attacker addresses across multiple chains in a coordinated sweep.
  5. Within hours, on-chain investigators including ZachXBT and Elliptic attributed the operation to North Korea’s Lazarus Group; the FBI formally confirmed attribution to TraderTraitor on February 26.

Every step in this chain was the intended behavior of the system. The keys signed because they were asked to. The multisig executed because the threshold was met. The Safe contract upgraded because the owners voted to upgrade it. The Safe Foundation’s subsequent statement is unambiguous: the forensic review “did NOT indicate any vulnerabilities in the Safe smart contracts.” None of these primitives malfunctioned.

Why “protect the key” is the wrong frame

Every dominant institutional-custody architecture is a variation on the same theme: make it harder for adversaries to obtain the signing material.

  • HSMs isolate the key inside tamper-resistant hardware so server compromise doesn’t leak it.
  • MPC (multi-party computation) distributes the key across parties so no single compromise reconstructs it.
  • TEEs (trusted execution environments) isolate the key inside silicon that’s opaque to the host operating system.
  • Hardware wallets hold the key in a separate device that requires a physical confirmation to sign.
  • Multisig requires a quorum so compromising one signer doesn’t move funds.

Every one of these would have signed the Bybit transaction. The attacker didn’t need to obtain a key. They didn’t need to defeat HSM isolation, or break an MPC ceremony, or extract from a Nitro Enclave. They needed to get the legitimate signers to sign the wrong message. The build-pipeline compromise gave them that.

The frame is wrong. The threat isn’t adversaries trying to obtain signing material. The threat is the system being asked to sign something other than what the operator intended. As Trail of Bits wrote five days into the incident, this is a threat-modeling failure as much as a technical one: the right lesson is to design for the attacker that doesn’t need the key.

What onchain policy enforcement would have done

Smart accounts can express constraints in code: an account whose constitution refuses certain calls regardless of who signed them. A Safe with an onchain policy module that allowed transfers only to allowlisted recipients, or only via specific function selectors — or that gated setImplementation behind an additional 24-hour timelock — would have caught the malicious upgrade transaction at the contract level, before any state changed.

For Bybit specifically: a policy that prohibiteddelegatecall to non-allowlisted implementations would have reverted the malicious transaction. The signing event would have been a non-event. The keys would have done exactly what they did. The damage would have been zero.

This is what we mean when we say policies belong on the chain, not on a vendor’s server. A policy engine that runs on a vendor’s infrastructure depends on the vendor’s infrastructure being trustworthy. A policy that lives in the smart account is enforced by the same machine that executes the call. Compromise the interface, the dashboard, the build pipeline, the operator machine — the chain still checks the rule.

The uncomfortable implication

The natural objection is that modern custody platforms — Fireblocks, Fordefi, Anchorage, Utila, Turnkey — don’t just hold keys; they ship policy engines: transaction rules, address allowlists, spending limits. Wouldn’t one of those have caught the Bybit upgrade? For most configurations the answer is no — the calldata wasn’t obviously prohibited — but that’s not even the deepest problem.

The deeper problem is where the policy runs. A vendor’s policy engine executes on the vendor’s own centralized infrastructure — the same class of system as the Safe{Wallet} build pipeline that was compromised to stage this attack. Compromise that service, or the interface and data that feed it, and you compromise the policy along with it. Policy and enforcement share a single point of failure: the attacker who can rewrite what the signer sees can, in principle, reach the thing that’s supposed to check it. Adding a vendor policy engine is a second lock on the same door.

That’s not a bug in any specific product. It’s the inevitable consequence of architecting custody around the key as the unit of defense, with the rules bolted on as an off-chain service. The interface tampering attack works against every architecture that trusts the interface — and against every policy engine that lives next to it.

The only way out is to make the smart account itself the enforcer. Put the constitution in the contract. Have the chain check it. The signer can be tricked. The interface can be compromised. The build pipeline can be subverted. The chain doesn’t care: if the calldata doesn’t match the rule, the call reverts.

What we ship

Zodiac is the module standard for this — onchain policy enforcement on top of Safe. Roles scoped to specific contracts, functions, parameter ranges, and caps. Multi-step bundles that fail clean if any precondition breaks. The bound lives on the chain. The signer can be compromised. The bound cannot.

It’s the architecture every institutional custody platform should have shipped by February 21. We expect to spend the next several years arguing for it. If you’re evaluating custody for an organization that moves real money — especially in the wake of an incident on your current setup — let’s talk.

Sources

Primary public sources informing this analysis. Dates are first publication unless noted; some have been updated.