The Bitget Hack

How forged instructions got valid signatures, which controls bound each step, and what no provider, DFNS included, prevents.

Thibault de Lachèze-Murel
Thibault de Lachèze-Murel

On 24 September 2026, Bitget’s signing service signed transfers worth about $387.5 million to addresses an attacker controlled. Nobody stole a key. The attacker took over the server that tells the signer what to sign and the signer did exactly as it was told. That distinction is the whole lesson of the incident and it’s why we wanted to write this piece. Over the next sections, we will walk through what is known, explain which controls failed and why, lay out what any institution running digital assets can do about it, and then describe how DFNS approaches each of those controls, together with the limits that apply to any vendor.

Two things before we begin. Bitget is the victim of a crime and nothing here is an analysis of people, it’s an analysis of controls. And both forensic reports are interim, so I have been careful to distinguish between what is confirmed by Bitget, by the forensic reports, or by the blockchain itself, and what is reported by analysts or the press.

What happened

Bitget describes its wallets in three tiers. Hot wallets pay customer withdrawals automatically. Warm wallets hold the float that refills the hot wallets when they run low. Cold wallets keep the majority of assets offline. By Bitget’s count, the theft came from 12 hot and warm addresses across 11 chains. The cold wallets were untouched, and Mandiant found no evidence that private keys were compromised. The attack unfolded in two phases: an initial compromise of a third party security product on 31 August, followed by the move into production and the theft itself on 24 September, more than three weeks later. Every row in the table below is confirmed.

Date and time (UTC)Event
31 AugEarliest malicious activity in the logs, inside a third party security product. The attacker has a foothold, but not yet access to production
24 Sep, 16:07The attacker is on the management console of a second product, under an internal employee’s identity, and moves from there to the production wallet job server
24 Sep, 17:49The forged withdrawal tool begins invoking Bitget’s withdrawal process
24 Sep, 18:31Test transfers of 93 TRX and 0.84 ETH, below alert thresholds
24 Sep, 18:58 to 19:01First large transfer, then seven transfers on five chains in 15 seconds
24 Sep, 19:05Reconciliation flags a discrepancy. Customer withdrawals blocked
24 Sep, 19:16Five transfers on five chains in nine seconds, about $203 million, mostly from warm wallets
24 Sep, 20:40Keys not yet cleared, so funds are swept to cold storage
24 Sep, 21:23Last transfer out
24 Sep, 21:44Signing service shut down
25 SepRoot cause found at 08:43. Law enforcement notified at 13:42
2 OctWithdrawals fully restored after a phased restart from 28 September

In terms of what was taken, XRP made up about 41% of the value, followed by ETH, stablecoins, tokenized gold, ZEC, BNB, AVAX and TRX. One estimate puts about 60% of the value as having left after the 19:05 alert, which is worth pausing on: most of the money was lost after the exchange already knew something was wrong.

As of 12 October, the vulnerability has been patched and the vendor feature involved has been disabled. About $1.1 million has been frozen, which is under 0.3% of the loss, and Bitget’s CEO has said he does not expect to recover much more. Bitget covered the loss from its Protection Fund, then restored the fund to above $300 million from company capital. On attribution, Chainalysis links the theft to actors associated with North Korea, Elliptic calls the link highly likely, and TRM calls it likely; no government has attributed it. The vendor whose product was exploited has not been named, no CVE has been issued, and the fuller report Bitget promised has not yet appeared.

How it worked

The attack is best understood as a chain of eight steps. Each one is ordinary on its own. Together they produced valid signatures on fraudulent transfers.

The attacker got in through a security product. On 31 August, a zero day, meaning a flaw the vendor did not yet know existed, let the attacker run a hidden script inside a service running on a node of what SlowMist calls Product A. That script read a database password from an environment variable, where it had been stored in plain text, and used it to connect to the database. This is confirmed.

It then used a second product and a borrowed identity. At 16:07 UTC on 24 September, the attacker was logged into the management console of a second product, Product B, under the identity of an internal employee. Mandiant found a web shell there, which is a script that gives an attacker a remote command prompt, as well as a command and control channel, meaning the attacker’s remote link back to the appliance. How the attacker obtained that employee’s identity is still under investigation, so this step is confirmed with one gap.

It moved sideways into the wallet tier. From Product B, the attacker reached the production wallet job server, the machine that builds Bitget’s withdrawal transactions, and deployed malicious packages onto it. Confirmed.

It forged the instructions. SlowMist recovered a withdrawal tool from deleted files on that server. The tool had been written specifically for Bitget’s wallet system. Inside its own code it forged the risk control parameters that would normally have stopped unusual withdrawals, built withdrawal requests, and invoked the normal withdrawal process, starting at 17:49 UTC. Confirmed.

The signer signed. Every stolen transfer carries a valid signature from the Bitget wallet it left, and each one sat in the normal queue alongside ordinary customer payouts. One detail gives the attacker’s transfers away in hindsight: customer payouts from the two Ethereum hot wallets used a gas limit, the fee budget on a transaction, of roughly 63,000, while the seven thefts used a fixed 100,000 or 200,000. Nothing else among the 35,678 transactions that week did. Confirmed.

It tested, waited, and drained. The 18:31 test transfers were sized to sit below the alert thresholds, and they raised no alert. A 0.65 ETH test from a warm wallet at 19:03 preceded the 19:16 burst, which took about $203 million in nine seconds. Nine seconds across five chains leaves no room for any human approver to intervene. Confirmed.

It covered its tracks. After each transfer, the attacker deleted the records that the forged commands left behind. After 21:22 it tried to edit withdrawal records directly in the database. Two fabricated BTC orders returned errors, and nobody has yet said why. Confirmed.

It cashed out. The assets that could be frozen went first: all 34.75 million USDT was sold for ETH within 20 minutes. The proceeds moved to Ethereum, sat for a while in round number wallets, and then went to Bitcoin through THORChain and Chainflip. Part of the ZEC entered a shielded pool, at which point tracing stops. Circle and Tether froze about $339,000 between them.

The dimensions of the failure

Bitget has not published the design of its signing pipeline, so what follows is an analysis of the controls that were seemingly absent, weak or bypassed:

Key management and signing architecture. The keys did not need to be stolen, and that is the central point. The signer accepted instructions from a host without verifying the authority behind them. There was no proof that a person intended the transfer, and nothing bound the destination and amount to an approved intent. Whatever protected the keys at rest did not matter because the trust boundary was the host sending the requests. It is also worth noting that the warm wallets, which held about 55% of the traced value by Bitquery’s classification, sat on the same automated path as the hot wallets. The float that was supposed to be one step removed from automatic payouts was on the same conveyor belt.

Infrastructure security. Two third party security appliances ended up on the path to the production wallet tier. One of them kept a database password in an environment variable. The other was a management plane with the ability to run tasks and push files to a wallet host. The attacker sat inside this tooling for at least 24 days without being detected, by the earliest surviving log entry.

What was absent: secrets kept in a vault rather than in environment variables, management planes treated as tier zero (the class of systems that can compromise everything else), and monitoring for lateral movement onto signing hosts.

Application security. The withdrawal pipeline trusted risk control fields that were asserted by the caller, which means the requester could tell the pipeline that a withdrawal had passed checks it had never passed. The job server also ran packages the attacker dropped onto it. The fixed gas limits suggest there was an internal transfer path with fewer checks than the customer path.

What was absent: execution allowlisting on the signing host, so that only approved code can run, and independent verification of policy outcomes after a request is made.

Identity and corporate security. The attacker used an employee’s identity together with high privilege credentials. Nothing in the reports shows a device bound factor or a step up check for privileged actions that would have told the employee apart from an attacker holding the same credential. Bitget says insider involvement has been preliminarily excluded.

What was absent: the controls in place could not distinguish between the employee and the attacker.

Policy and governance. The test transfers were sized under the alert thresholds, and the risk parameters could be forged by the requester. There was no allowlist of approved destinations to bind where hot and warm wallets could pay: on every EVM chain, the money went to a single fresh address. No approval quorum stood between a request and a signature on the warm tier. Bitget now requires multiple approvals for critical operations.

General principle: a policy that the requester can assert is a label, not a control.

Detection and response. Reconciliation flagged the gap about six minutes after the first large transfer, which is a reasonable detection time. The response, however, hit the wrong switch. Blocking customer withdrawals did nothing, because the attacker was not a customer, and signing ran for another 2 hours and 39 minutes. The 20:40 sweep of funds to cold storage assumed the keys had been stolen, but the chain was saying otherwise: an attacker who holds the keys signs on its own machines and has no use for the victim’s withdrawal queue.

What was absent: The transfers were coming out of Bitget’s pipeline, that was the first question to ask.

GRC and process. Bitget says it runs regular third party pentests and publishes proof of reserves. Neither of those catches a zero day in a vendor product, and neither catches a signer that trusts its host.

What would have caught this: an architecture review that asks the plain question of what the signer verifies before it signs, an inventory of every tool that can run code on signing hosts, and a tabletop exercise built around a forged instruction coming from inside.

What wouldn’t have caught it: SOC 2, ISO 27001, or a pentest scoped to the application perimeter.

What to do about it

The measures below are in order of impact, and most of them apply to any institution that signs transactions, whatever provider it uses.

  1. Make the signer verify authority, not origin. Before any signature is produced, require proof that a named person intended this specific transfer and that the policy in force approved it, with that proof bound to the destination, the asset and the amount.
  2. Separate the policy decision from the tier an attacker would land on. If the same host that builds transactions also decides whether they are allowed, one compromise defeats both.
  3. Put a client controlled credential in the loop. A passkey lives on a person’s device, and no job server can forge one.
  4. Bound the operational tiers: allowlists that cover internal moves as well as external ones, a quorum of people on the warm tier, and velocity limits, meaning caps on amount and count over a time window, per wallet.
  5. Rehearse a signing stop that is independent of the customer withdrawal switch, and write down the first triage question in advance: are these transfers coming out of our own pipeline?
  6. Reconcile every outbound transaction to a ledger order within seconds, and alert on any unmatched transaction at any size.
  7. Treat security and management tooling as tier zero. Inventory anything that can run code on signing hosts, and remove the reach you do not need.
  8. Harden the signing hosts: only signed images run, no secrets in environment variables, and logs shipped off the host.
  9. Put every change to policies, permissions and allowlists behind a quorum that excludes the person who initiated the change.

Then look back 90 days for four indicators: security product services spawning scripts, console logins under staff identities at odd hours, new files appearing in management platform web roots, and task parameters containing shell syntax.

How DFNS approaches it

For each control below, we disclose which deployment model it holds in, as the answer varies:

Key management and signing architecture. DFNS generates and uses keys with MPC by default, which means the private key never exists in one place. But key theft is not what happened at Bitget, so the more important question is what has to be true before a DFNS signer agrees to participate in a signature. Two independent checks sit in front of it. The first is a User Action Signature: the user signs the request on their own device, with a passkey or with an API signing key the client holds, over a challenge that is bound to the request itself, and the authentication service verifies that signature before the request goes any further. The second is the Governance Engine, which does not take the authentication service’s word for it. It independently re-validates the User Action Signature, and then re-verifies, against tamper evident records, that the credential is registered, that the user holds the permission, that the policy in force was approved by the required quorum, and that the amount, asset, source, destination and time match the governed history. Only then does it issue a signing integrity proof over the digest of the exact payload to be signed. The signer itself checks that proof, and signs nothing without it. In the DFNS hosted model, this is an integrity check performed by DFNS. In hybrid and on premise deployments, the client runs the signers and the Governance Engine itself, and the same verification becomes a veto that holds even if the DFNS control plane, the services that take requests and evaluate policy, were compromised.

Infrastructure security. No third party product can initiate, approve or complete a signature on DFNS. Signing infrastructure runs on AWS, with the keys that encrypt key shares held in KMS and Secrets Manager. Our monitoring vendor has read only access and no access to user data. Human access to the control plane is brokered just in time, requires dual approval, is capped at one hour and is recorded. In hybrid and on premise deployments, DFNS has no access to the client’s infrastructure or keys at all.

Application security. The Policy Engine evaluates every transfer, broadcast and signature request before signing, and it fails closed wherever a recipient or amount cannot be determined, for example on contract calls and raw signatures. The key that signs the integrity proof sits outside the API tier, so a compromise of the API tier alone cannot mint a valid proof. In hybrid and on premise deployments, the Validation Gate goes further: before every signature, it submits the key identifier, the public key and the payload digest to an endpoint the client operates, and proceeds only on approval. A rejection, an error or an unreachable endpoint all stop the operation.

Identity and corporate security. Every action is signed on the user’s device. An inserted credential fails because the Governance Engine checks it against the governed credential records, and an edited permission row grants nothing, for the same reason. Service accounts carry scoped API signing keys rather than standing privilege.

Policy and governance. The platform provides recipient allowlists, amount limits, velocity limits by amount and count over windows from one minute to thirty days, evaluated per source wallet, and approval quorums. Internal transfers are exempted explicitly rather than by default, either by allowlisting internal addresses or by scoping policies by wallet tag. Changes to policies, permissions and address book entries sit behind approval groups in which M of N named approvers must agree, with the initiator excluded by default.

Detection and response. The client can stop signing with no DFNS action in the path, either with a block policy scoped to a freeze tag or with an organization-wide block on every transfer, broadcast and signature request, taking effect on the next signing request. For reconciliation, DFNS provides signed webhooks, which are event notifications pushed to the client’s systems, for transfer, signature, policy and onchain events. Transfer Screening with KYT services and runs as policy rules before signing.

GRC and process. DFNS holds SOC 2 Type I and II and ISO 27001, 27017 and 27018. It keeps an audit log in which every user action signed event is stored with the credential’s public key and signature, verifiable by the client with a published verifier. Governance Engine records alarm on divergence and mirror into the client’s own logs. Incidents run on a five level severity model with a named Incident Commander, status.dfns.co for platform incidents, and an addendum under DORA (Digital Operational Resilience Act), committing DFNS to cooperate with the client’s regulators.

Dangers, limits and nuances

This is the section most vendor analysis leaves out, and it is the one I would want to read. Everything in it applies to any provider, whatever its architecture.

What no provider prevents. A valid credential in the wrong hands signs. If an attacker controls an approver’s unlocked laptop, or holds a service account’s key that has transfer permission with no quorum behind it, the platform will sign and will record that the credential did so. A platform can bind a signature to a stated intent. It cannot know whether the intent was genuinely the person’s, or whether the approver actually read what they approved. Quorums, allowlists and transaction summaries narrow that window considerably. They do not close it.

Where responsibility sits with the client. Every control above is configuration. An allowlist that was never set, a velocity limit that was never tuned, an internal address exemption drawn too wide, a quorum of one, a freeze tag that too many people can remove, etc. Each of these is the client’s decision, and each reproduces part of what failed at Bitget. Reconciliation is the client’s to build from the hooks a provider exposes. A client that scopes policies off its operational wallets for the sake of speed has chosen Bitget’s trade, whether or not it thinks of it that way.

Where every hosted model has residual risk. Whenever policy evaluation and proof generation run in the provider’s own infrastructure, the proof that a signature was authorized is an integrity check performed by the provider, not an independent veto held by the client. The failure mode of that model is a compromise that reaches both the provider’s API tier and the key that signs the proofs. Good providers make that path require collusion among several staff and monitor it heavily, which makes misuse provable and noisy rather than impossible. The cryptographic guarantee that a provider alone can never sign comes only from deployments where the client runs the signers and the verification itself, hybrid or on premise. Every provider also runs third party software, with vendors in the path for monitoring and access brokering, and every provider will say that none of it can produce a signature. Bitget’s vendors could not produce one either, until a zero day turned one of them into a path to the host that could. The right answer is a trust boundary at the signer, not an absence of vendors, and the right question to ask any provider is where exactly that boundary sits.

Policy change usually has no time lock. On most platforms, changes take effect as soon as the quorum approves, and a quorum can be socially engineered in an afternoon. Lifting a freeze is often controlled by permission, not by quorum. Ask how yours works, and restrict who holds those rights.

Insurance is not a backstop. Provider insurance, where it exists, is typically a cyber crime policy and a professional liability policy, with aggregate limits in the millions and deductibles per event, responding to theft caused by an external breach of the provider’s systems or by fraud by the provider’s staff. Against a loss of Bitget’s size, that is a liability instrument, not a recovery plan. More importantly, a forged instruction originating from the client’s own compromised credential, with no breach on the provider’s side, generally falls outside the insuring clause. The client’s own crime cover is the right instrument for that case, and the insurer decides on any specific loss. The practical advice is to read the insuring clause of every policy involved and decide now what would cover yours.

Speed against control. Every control above adds either latency or a person. A quorum on the warm tier means nobody sweeps funds at two in the morning. Fail closed means that an outage of your validation endpoint stops your payouts. Bitget’s pipeline was fast because it trusted its host. A bank has to decide which flows may be fast, bound those flows with allowlists and limits, and accept that the rest are slow.

A checklist for this week

  • Check signer authority: Does it verify intent or just request origin?
  • Map access: Remove unnecessary paths to signing hosts.
  • Separate trust domains: Keep policy decisions and transaction building apart.
  • Test detection: Send a small canary transfer and time the alert.
  • Test the kill switch: Time alert-to-halt with the on-call person.
  • Audit exceptions: Review internal transfers exempt from limits and allowlists.
  • Hunt back 90 days: Follow the process in section 4.
  • Check insurance: Does coverage include forged internal instructions?
  • Set governance: Review policy-change quorums and who can lift a freeze.

Appendix: each attack step, the control that bounds it, and where the control stops

Attack step at BitgetWhat bounds it on DFNSWhere the control stops
Zero day in a security product with reach into the signing tierNo product in the path can initiate, approve or complete a signature. Management planes isolated, human access just in time and recorded, signers run signed images only.A zero day in a product DFNS runs remains possible. In SaaS the client inherits the DFNS supply chain. The boundary is at the signer, not in the absence of vendors.
Employee identity reused on a management consoleEvery action signed on the user’s device over a request bound challenge. Credentials and permissions held as governed records. Service accounts carry scoped keys.An attacker on an unlocked approver device, or holding a service account key with transfer permission and no quorum, signs. The platform records which credential acted, not whose hands.
Attacker packages on the wallet job serverThe signer participates only on a signing integrity proof from the Governance Engine, which has itself validated the User Action Signature and the governed records. A host that sends requests is not trusted on its own.In SaaS the proof is an integrity check, not an independent veto. A compromise reaching both the API tier and the engine’s signing key defeats it.
Risk parameters forged in the requestPolicy evaluated before signing and re-verified by the Governance Engine, which validates the User Action Signature and the governed records before issuing the proof the signer requires. In hybrid and on premise, the Validation Gate lets the client’s own system approve each digest, failing closed.Validation Gate applies only to signers the client runs. Fail closed means a dead endpoint stops payouts.
One fresh attacker address per chainRecipient allowlists. Rules fail closed where the recipient cannot be determined.Allowlists are configuration. Internal exemptions drawn too wide reproduce the Bitget path. Allowlist changes need a quorum but have no time lock.
Five chains in nine seconds, no human approverApproval quorums. Velocity limits by amount and count per source wallet.Many wallets mean many budgets unless policies are scoped by tag. Approvers can approve without reading.
Tests sized below alert thresholdsCount based velocity rules. Allowlists bind destinations regardless of amount.A count rule not configured is no rule. Thresholds are the client’s.
Withdrawal records written directly into the wallet systemA written record carries no valid User Action Signature for the authentication service to accept and earns no proof from the Governance Engine, so the signers do not participate.In SaaS the proof key sits outside the API tier. A compromise reaching both defeats it.
Signing ran 2 hours 39 minutes after the alertClient triggered block, by freeze tag or organisation wide, effective on the next signing request. Credentials deactivated, permissions revoked, pending approvals rejected.Outbound only. Nothing already broadcast. Lifting a tag freeze is permission controlled. Someone still has to decide to press it.
Reconciliation caught only the large transferSigned webhooks with retry and replay, history endpoints, vault balance ledgers, idempotency via external identifiers.Reconciliation logic is the client’s to build.
Policy change or credential insertion to clear the pathPolicies, permissions and address book entries behind M of N quorums, initiator excluded. The engine proves which policy was in force.No time lock or delayed activation. A quorum can be socially engineered.
Attacker deleted its tracesAudit log of every signed action, verifiable with a published verifier. Governed records mirrored into client logs with a tamper alarm.Logs prove, they do not prevent. The tamper alarm fires after the fact.
Cash out, under 0.3% recoveredTransfer Screening before signing. Incident process with a named Incident Commander and a DORA addendum.Screening cannot recover what has left. Sub 1% recovery is the baseline for this class of attacker.
Audits and penetration tests missed the design flawSOC 2 Type I and II, ISO 27001, 27017, 27018. Vendor inventory and auditor tested vendor scope statements.Certifications attest process, not the absence of a zero day. “What does the signer verify” is an architecture review, not an audit control.

Sources

Related articles

Contact us