How Forged Requests Took $387.5M Without the Keys
On September 24, 2026, an attacker moved about $387.5 million out of Bitget’s hot and warm wallets. On the evidence so far, it never held a private key. It reached a production wallet server through two third-party security products, ran a purpose-built withdrawal tool that forged the risk-control fields, and let Bitget’s own signing service sign the transfers. The first trace of the intrusion dates to August 31, and about 61 percent of the value left after Bitget’s own alert had fired.
This briefing expands on our published summary. It draws on the Mandiant and SlowMist reports released on September 30, Bitget’s disclosures and the public on-chain record, as of October 2, 2026. Both forensic reports are preliminary, so we separate what the reports state, what we infer and what is still unknown.
Timeline
The attacker was inside Bitget’s security tooling at least 24 days before the theft. The theft itself ran 2 hours 52 minutes, and the signing service stayed up for 2 hours 39 minutes after the first alert. Clock times are UTC, converted from the UTC+8 that SlowMist and Mandiant use. Entries with a date only keep the source’s date.
| When (UTC) | Event | Source |
|---|---|---|
| Aug 31 | Earliest malicious activity in the available logs. A zero-day in a service on one Product A node runs a hidden script, reads the database password from an environment variable and connects to the database. | SlowMist |
| Sep 23 | The same hidden-script activity appears on a second Product A node. | SlowMist |
| Sep 24, 16:07 | The attacker works on Product B’s management platform under an internal employee’s identity: three command-injection attempts through task parameters, then code through the web execution endpoint. | SlowMist |
| Sep 24, no time or zone given | Web shell and command-and-control on appliance B, lateral movement to the production wallet job server, malicious packages deployed. | Mandiant |
| Sep 24, 17:49 | The withdrawal tool starts running on the wallet host. | SlowMist |
| 18:31:00 | First transfers: 93 TRX, then 0.84 ETH eleven seconds later. No alert fires, by the CEO’s account. | SlowMist, Bitquery, Bitget |
| 18:58:59 | First large transfer: 34.75 million USDT on Ethereum. | Bitquery |
| 19:01:20 to 19:01:35 | First burst: seven transfers on five chains in 15 seconds, $87.6 million. | Bitquery |
| 19:03 | A 0.65 ETH test from 0xffa8…cd54, then 17,197.72 ZEC. | Bitquery |
| 19:05 | Bitget’s reconciliation system flags the discrepancy. Risk control blocks withdrawal requests across the platform. The CEO specifies user-initiated withdrawals. | Bitget |
| 19:14 | Bitget declares its highest-level emergency response. | Bitget |
| 19:16:14 to 19:16:23 | Second burst: five transfers on five chains in 9 seconds, $203.1 million. Four come from wallets Bitquery treats as warm. | Bitquery |
| 19:40 | Containment begins. | Bitget |
| 20:09 | Two more ETH transfers, 3,275 ETH in total. | Bitquery |
| 20:40 | The wallet team starts moving funds to cold wallets. Key compromise is not yet ruled out. | Bitget |
| 20:55 to 21:23 | Final wave: USDC on Avalanche, XRP, ZEC and ETH, plus ALGO, TIA and ATOM by Bitget’s count. | Bitquery, Bitget |
| After 21:22 | The attacker tries to modify withdrawal records in the wallet database and to invoke withdrawal tasks on the host. Two fabricated BTC orders enter processing and return errors. | SlowMist |
| 21:23:11 | Last traced transfer: 223.20 ETH. | Bitquery, SlowMist |
| 21:44 | Wallet withdrawal services, including signing, are shut down. | Bitget |
| Sep 25, 08:43 | Bitget identifies the root cause. | Bitget |
| Sep 25, 13:42 | Bitget reports the incident to law enforcement. | Bitget |
| Sep 28, 08:00 | BTC withdrawals reopen. ETH follows on September 29 and USDT on September 30, with the remaining tokens, fiat and P2P scheduled for October 2. | Bitget |
| Sep 30, 05:20 | Mandiant and SlowMist preliminary reports are published. | Bitget |
SlowMist also records hidden-script activity on a third Product A node, dated September 25 in UTC+8.
Attack chain
On the evidence so far, the attacker did not need the keys, because it took over a host that tells the signer what to sign. Each stage below states what the forensic reports say, then what we infer from it.

Figure 1. Attack path: two security products, five pipeline stages.
The diagram is our reconstruction, because Bitget has not published its pipeline. Read it from the top left: the attacker’s lane joins the withdrawal path at the wallet job server, below risk control, so the controls above that line never saw its requests.
1. Entry through security tooling
Both forensic teams trace the intrusion to two third-party security products running in Bitget’s production environment. SlowMist calls them Product A and Product B, and Mandiant calls them security appliances A and B. Neither names the vendor, and no CVE, advisory or patch is public as of October 2. We assume both reports use A and B for the same two products, and that SlowMist’s wallet application host is Mandiant’s wallet job server. Neither report says so.
On Product A, a zero-day in a service on one node let the attacker run a hidden script under the service process, read the environment variable holding the database password and connect to the database. The earliest instance in the available logs is August 31. The same pattern appears on two more nodes on September 23 and 25.
Bitget’s incident page says the attacker may have exploited the vulnerability to obtain high-level internal credentials. Its CEO stated the same without the hedge on September 28. SlowMist has not yet established how the attacker moved from Product A to Product B, so the link between that database and the identity used next is unconfirmed.
2. From the console to the wallet server
At 16:07 UTC on September 24 the attacker was on Product B’s management platform under an internal employee’s identity. It tried three times to inject system commands into task parameters to write malicious files. It then submitted code through the platform’s web execution endpoint in an attempt to change server configuration, write a communication relay file, and upload and assemble malicious program files in batches.
Mandiant reports a web shell and a command-and-control channel on appliance B. From that foothold the attacker moved to the production wallet job server and deployed malicious packages. Mandiant’s summary is that the appliances were used to distribute those packages and take control of the server.
Our reading: Product B is a management plane that can run tasks on the hosts it manages and push files to them. That reach turned a compromised security console into code execution on a wallet server. A relay file of that kind would be consistent with a tunneling web shell.
3. The withdrawal tool
SlowMist recovered the tool from files the attacker had deleted. It was written for Bitget’s wallet system: it forged the risk-control parameters in its own code, built withdrawal requests and invoked the withdrawal process. Host logs show it starting at 17:49 UTC, 42 minutes before the first transfer.
Our reading, in three points:
- A tool tailored to the withdrawal logic means the attacker studied the wallet system’s internals before the day of the theft. That fits a dwell time of weeks.
- A forged risk-control parameter only works if the component that receives it trusts the caller. The risk decision appears to have traveled inside the request as a field, with nothing downstream verifying it independently.
- On the EVM chains every traced transfer paid a single attacker address. Nothing in the signing path appears to have restricted where hot and warm wallet funds could go.
4. Execution
The tool tested first: 93 TRX and 0.84 ETH, eleven seconds apart. Bitget’s CEO said both sat below the risk-control thresholds and raised no alert. Twenty-eight minutes later it sent 34.75 million USDT, then hit five chains in 15 seconds.
At 19:03 a 0.65 ETH test left 0xffa8…cd54, a wallet Bitquery treats as warm. At 19:16 a 9-second burst took $203.1 million, $196.1 million of it from that wallet and a second warm wallet on the XRP Ledger. As we read it, the sequence repeats: test a tier, wait, drain it.
5. Database tampering and cleanup
After 21:22 UTC the attacker also tried to modify withdrawal records directly in the wallet database and to invoke withdrawal tasks on the host. Two fabricated BTC withdrawal orders entered processing and returned errors, and the logs show the attacker reviewing logs, querying order status and retrying.
BTC is not among the assets Bitget lists as stolen. Neither report says why those orders failed. If a control rejected them, it is the one point where the pipeline refused, and the final reports should say which.
The attacker deleted its tooling and, according to Bitget’s CEO, the traces of the fraudulent commands. SlowMist recovered the tool from the deleted files.
What the on-chain record shows
Bitget’s own wallets signed every transfer, and the transfers do not look like customer payouts. That matches the forensic account: valid signatures on forged instructions.
Bitquery traced 23 transfers on nine chains, worth $386.80 million at the hour each one left. Bitget’s count is about $388 million across 11 chains and 12 wallet addresses, and it includes ALGO, TIA and ATOM transfers outside the public reconstruction. XRP was the largest asset: 102.98 million XRP, about $157.8 million, or 41 percent of the traced value.
Four details matter:
- Ordinary signatures in the ordinary queue. On Ethereum a theft sits between two customer payouts, with the next nonce and the same fee settings. The XRP transfers carry the same signing key and fee as a routine Bitget move made that morning.
- Gas limits that customer payouts never use. Customer ETH withdrawals from the two Ethereum hot wallets used a gas limit near 63,000. The seven theft transfers from those wallets used a fixed 100,000 or 200,000, and nothing else among the 35,678 transactions they sent in eight days did.
- A Zcash sweep. The Zcash hot wallet never used more than 54 inputs in thousands of earlier payments. The two theft transactions used 1,074 and 956, and took almost everything the wallet held.
- Warm wallets on the same path. Two wallets that Bitquery treats as warm sent about $214 million, 55 percent of the traced value. Five chains in nine seconds leaves no room for a human approver.
One of the two, 0xffa8…cd54, is marked cold in Bitquery’s own labels and by public trackers. Bitquery treats it as warm because it sends routine transfers most days and tops up the hot wallets. Bitget states that no cold wallet was affected.
The fixed gas limits show that the code that builds customer payouts did not build these transactions. Either the tool set the parameters itself or it used an internal transfer path. Bitquery notes that Bitget’s warm wallet uses fixed limits like these for its own internal transfers, which favors the second. The forensic reports do not say.
They also show what was detectable. The 0.84 ETH test at 18:31:11 already carried the anomalous gas limit. A monitor comparing each outbound transaction against the wallet’s own baseline had a signal 34 minutes before the reconciliation alert.
Where the controls failed
We map the incident onto the gates of a standard withdrawal pipeline. Bitget has not published its design, so the gate model is ours. The attacker entered below most of the gates, and nothing downstream caught the forgery.
| Gate | What happened | Threat | Basis |
|---|---|---|---|
| Initiation | The tool built withdrawal requests on a wallet host, outside the customer flow. Bitget’s CEO says the attacker used legitimate credentials. | Spoofing | SlowMist, Bitget’s CEO |
| Intent capture | No report shows a customer order behind any transfer. Later the attacker tried to write withdrawal records straight into the wallet database. | Tampering | SlowMist, our inference |
| Policy check | The tool forged the risk-control parameters in the requests it built. The test transfers also sat below the alert thresholds. | Tampering | SlowMist, Bitget’s CEO |
| Approval | Nothing human stood between the request and the signature, for hot or warm wallets. Bitget now requires multiple approvals for critical operations. | Elevation of privilege | Inferred from timing, Bitget |
| Signing | The transfers carry valid signatures from Bitget’s own wallets. Bitget has ruled out private-key compromise on the investigation to date. | Elevation of privilege | Bitquery, Bitget |
| Submission | Transactions went out in the normal queue, in order with customer payouts. | None, worked as designed | Bitquery |
| Reconciliation | Flagged the gap at 19:05, about six minutes after the first large transfer. The response blocked user-initiated withdrawals and left signing running. | Detection worked, response missed | Bitget |
| Audit trail | The attacker deleted its tool and the traces of its commands on the host. | Repudiation | Bitget, SlowMist |
Read together, the rows point to one design flaw. The signer appears to have trusted the wallet host, so whoever controlled that host inherited the authority of the whole pipeline.
Bitget is the latest in a run of exchange losses where the keys stayed safe and the instruction was forged. DMM Bitcoin and Bybit started in a vendor’s systems, and SlowMist described BigONE as a supply chain attack.
| Incident | Loss | Layer the attacker controlled |
|---|---|---|
| DMM Bitcoin, May 2024 | $308 million | A legitimate transaction request, manipulated through a compromised wallet software vendor |
| Bybit, February 2025 | About $1.5 billion | The signing interface, altered by code injected from a compromised vendor developer machine |
| BigONE, July 2025 | $27 million | Production account and risk-control servers, whose logic was modified |
| Bitget, September 2026 | About $387.5 million | The wallet job server, where a custom tool forged the risk-control parameters |
Detection and containment
Detection took about six minutes from the first large transfer, seven by Bitget’s count. The signing stop took another 2 hours 39 minutes, and about $238 million of the $387 million traced left in that window.

Figure 2. Bitquery transfer record and Bitget response timeline, September 24, 2026, UTC.
Values use Bitquery’s pricing. Where it gives only a chain total, we apportioned it across that chain’s transfers by amount.
Bitget’s reconciliation system flagged the discrepancy at 19:05 UTC. By then about $149 million had left, nearly all of it in the previous six minutes. By the CEO’s account, the automatic response blocked user-initiated withdrawals across the platform. The attacker’s requests did not come through that path, and the transfers continued.
At 19:14 Bitget declared its highest-level emergency response. Two minutes later the second burst took $203.1 million in nine seconds. Transfers continued until 21:23, and the withdrawal and signing services went down at 21:44.
Bitget’s own timeline explains the delay. At 20:40 it had still not ruled out key compromise, and it began sweeping hot wallet balances to cold storage. Under that hypothesis a sweep is the logical move and stopping the signer is not.
The chain already contradicted the hypothesis. The thefts came out of Bitget’s own queue, in nonce order, between customer payouts. An attacker holding the keys signs on its own infrastructure and has no use for the victim’s queue.
So the first triage question in a wallet incident is whether the bad transactions are coming out of your own pipeline. Signer logs answer it in minutes. If they are, the response is to stop signing on every chain, and a stop at 19:05 would have kept about $238 million.
Funds and attribution
Almost none of the money is coming back, and the North Korea link is likely but unconfirmed.
The attacker dealt with the freezable assets first. All 34.75 million USDT was sold for ETH within 20 minutes of arriving, and the USDC and XAUt followed within about half an hour. Funds on Arbitrum, Avalanche, Optimism and Base were bridged to Ethereum. By September 26 the ETH sat in eight holding wallets, six of them with 10,000 ETH each.
| Asset | Where it went | As of |
|---|---|---|
| XRP, 102.98 million | All moved by September 27. 90.5 percent swapped for Bitcoin through THORChain, most of the rest for ETH. | Sep 29 |
| ETH, 68,295 in holding wallets | Seven of the eight wallets paid out from September 27: 29,088 ETH into THORChain for Bitcoin, most of the rest through Chainflip and bridges to Arbitrum, Noble, Solana and Tron. 10,000 ETH had not moved. | Sep 29 |
| BNB and TRX | Swapped to Bitcoin through THORChain and Chainflip within a day. 126.71 BTC had been paid out by 14:38 UTC. | Sep 25 |
| ZEC, 18,916.72 | 3,259 ZEC moved into Zcash’s Ironwood private pool, where the trail ends. 15,657 ZEC still in the attacker’s address. | Sep 30 |
| Frozen | Close to $1.1 million, by Circle, Tether and NEAR Intents. That is under 0.3 percent of the loss. | Oct 1 |
THORChain declined Bitget’s request to block the attacker’s addresses, citing its permissionless design. Bitget’s CEO told CNBC, in an interview published on October 2, that she is not expecting to recover a lot. Bitget says its Protection Fund covers the loss and that customer balances are unaffected.
On attribution, the analytics firms agree and the forensic firms are silent. Elliptic rates a North Korea link as highly likely, citing on-chain ties to an earlier DPRK-attributed theft and to addresses used to launder the Bybit proceeds. TRM Labs calls it a likely North Korea attack, ties the laundering to a network TraderTraitor has used, and states that it has not definitively attributed the attack. ZachXBT reports that one of the launderers also moved funds from the Kelp DAO exploit of April 2026.
Bitget’s CEO has called North Korean involvement very likely, citing IP addresses that match VPN services used by a North Korean group. Bitget’s official position is that it will not speculate on attribution without verified findings, and Mandiant’s status report makes none. We found no government attribution as of October 2.
Our working assumption is TraderTraitor, the cluster the FBI named for DMM Bitcoin and Bybit. The tradecraft fits: weeks of quiet access, a tool written for one victim’s wallet system and the same laundering routes.
Open questions
Both forensic reports are interim. Seven questions decide how far the lessons generalize.
- Which products are A and B? No vendor, CVE, version or patch is public, so nobody can check exposure by product name.
- How did the attacker get from Product A to Product B, and how did it obtain the employee identity? SlowMist is still investigating movement between the systems.
- When did the intrusion start? Mandiant dates privileged access to the appliances to September 24. SlowMist’s logs show activity on August 31, and earlier logs may not exist.
- Why did the two fabricated BTC orders fail, and did a control reject them?
- What did the signing service verify about a request, if anything, beyond where it came from?
- When did the first alert fire? Mandiant’s background section puts detection at 18:31 UTC. Bitget’s CEO says the tests raised no alert and detection came at 19:05.
- What data did the attacker reach? It connected to a database on August 31 and reached the wallet database on September 24, and no report addresses customer or operational data.
What to test in your own environment
There is nothing you can patch today. Bitget has notified the vendor and disabled the affected functionality, but no vendor, CVE or version is public. The work is to prove that a forged request from inside your wallet tier would not be signed. The eight controls below are ordered by impact, each with the evidence that shows it works. P is preventive, D is detective and R is responsive.
| # | Control | Type | Owner | Evidence that it works |
|---|---|---|---|---|
| 1 | The signer verifies the policy decision itself. Before signing, it checks an approval signed by the risk engine and bound to destination, asset, amount and order ID. It accepts no risk-control fields asserted by the calling host. | P | Wallet engineering | A request sent from a wallet host with fabricated policy fields is rejected and logged. |
| 2 | Destinations are enforced inside the signing boundary. Transfers that skip customer risk checks, such as sweeps and rebalancing, pay only allowlisted addresses. Allowlist changes need a time-locked quorum with out-of-band confirmation. | P | Wallet engineering, CISO | An internal-type transfer to a new address fails at the signer. |
| 3 | Warm wallets carry their own controls: an approval quorum of two or more people, out-of-band verification and a value cap per transaction and per time window, all enforced at the signer. | P | Custody operations | A warm transfer above the cap cannot be signed without the quorum. |
| 4 | A signing stop that works in minutes. One rehearsed action halts signing on every chain. It is independent of the wallet backend and of the customer withdrawal switch, and the on-call incident commander is authorized to use it. | R | Security operations, CISO | A drill record with the measured time from alert to stop. |
| 5 | Reconciliation per transaction. Every outbound transaction from an operational wallet is matched to a ledger order within seconds. An unmatched one raises the top-severity alert at any amount. | D | Security operations | A canary transfer of a trivial amount raises the alert, with the time to alert measured. |
| 6 | Security and management tooling treated as tier 0. Inventory every product that can run tasks, push files or open a shell on wallet and signing hosts. Remove that reach where it is not needed and isolate the consoles. | P, D | Infrastructure, CISO | The inventory, plus a test showing the console cannot execute code on a wallet host. |
| 7 | Wallet hosts run only known code: execution allowlisting, file integrity monitoring and alerts on new processes and packages on job servers. | P, D | Infrastructure | An unsigned binary dropped on a wallet host does not run and raises an alert. |
| 8 | Secrets and logs out of reach. No database passwords in environment variables, database audit on withdrawal tables and logs shipped off-host to immutable storage. | P, D | Infrastructure, Security operations | Credentials are short-lived and vault-issued, and a log deleted on the host still exists centrally. |
Controls 1 to 3 each close the path the tool used. Controls 4 and 5 together could have stopped the theft at the test transfers, provided the forged requests left no matching ledger order. The reports do not settle that. Controls 6 to 8 address the 24 days the attacker spent inside.
None of the first five holds if the same management console also reaches the risk engine and the approvers’ devices. Control 6 decides how much the others are worth.
Run a retrospective hunt as well. The earliest trace at Bitget predates the theft by 24 days, so look back at least 90 days for four things:
- security-product services spawning scripts or reading environment variables
- console logins under staff identities at unusual hours
- new files in the web roots of management platforms
- task parameters containing shell syntax
Appendix: the traced transfers
Bitquery traced 23 transfers out of Bitget wallets on September 24, 2026, and checked each one against a public node for its chain. Values are priced at the hour of each transfer, and XRP at the price in the five minutes around each transfer.
| Chain | Assets | Value |
|---|---|---|
| XRP Ledger | XRP | $157.8 million |
| Ethereum | ETH, USDT, USDC, XAUt | $126.6 million |
| Zcash | ZEC | $29.4 million |
| Arbitrum | USDT0 | $19.7 million |
| Avalanche | AVAX, USDC | $16.8 million |
| Optimism | ETH | $15.4 million |
| BNB Smart Chain | BNB | $9.9 million |
| Tron | TRX | $7.0 million |
| Base | ETH | $4.2 million |
| Total, nine chains | $386.80 million |
The transfers in order. Warm marks the two wallets Bitquery treats as warm.
| Time (UTC) | Chain | Bitget wallet | Amount |
|---|---|---|---|
| 18:31:00 | Tron | TJ7hhY…JSdb | 93.00 TRX, test |
| 18:31:11 | Ethereum | 0x1ab4…8f23 | 0.84 ETH, test |
| 18:58:59 | Ethereum | 0x1ab4…8f23 | 34.75 million USDT |
| 19:01:20 | Arbitrum | 0x1ab4…8f23 | 19.67 million USDT0 |
| 19:01:23 | Ethereum | 0x1ab4…8f23 | 12.85 million USDC |
| 19:01:23 | Ethereum | 0x5bdf…f7ef | 3,000.32 XAUt |
| 19:01:29 | Optimism | 0x5bdf…f7ef | 5,737.68 ETH |
| 19:01:32 | XRP Ledger | rGDreB…GzFn | 2.25 million XRP |
| 19:01:33 | Base | 0x97b9…8689 | 1,555.32 ETH |
| 19:01:35 | Ethereum | 0x1ab4…8f23 | 7,130.86 ETH |
| 19:03:23 | Ethereum | 0xffa8…cd54, warm | 0.65 ETH, test |
| 19:03:49 | Zcash | t1Xd1L…JriY | 17,197.72 ZEC |
| 19:16:14 | BNB Smart Chain | 0xffa8…cd54, warm | 12,719.46 BNB |
| 19:16:15 | Avalanche | 0xffa8…cd54, warm | 821,011.97 AVAX |
| 19:16:18 | Tron | TJxe1M…UGx5 | 20.59 million TRX |
| 19:16:20 | XRP Ledger | rwTTsH…XLEf, warm | 91.42 million XRP |
| 19:16:23 | Ethereum | 0xffa8…cd54, warm | 13,965.93 ETH |
| 20:09:11 | Ethereum | 0x1ab4…8f23 | 1,879.20 ETH |
| 20:09:23 | Ethereum | 0xffa8…cd54, warm | 1,395.90 ETH |
| 20:55:07 | Avalanche | 0x1ab4…8f23 | 8.20 million USDC |
| 21:19:21 | XRP Ledger | rwTTsH…XLEf, warm | 9.31 million XRP |
| 21:19:55 | Zcash | t1Xd1L…JriY | 1,719.00 ZEC |
| 21:23:11 | Ethereum | 0x1ab4…8f23 | 223.20 ETH |
Bitquery identifies four first-hop attacker addresses. TRM Labs independently lists the EVM address.
- EVM chains: 0x770b10b273fC44Fe9197D6bF20F145c2e98463Ee
- XRP Ledger: rwNhefsz1UQEusxhCvHip3RANinWi4CTck
- Tron: TBWNguTTgezw9dVorX441C6nDrZpRxYwKD
- Zcash: t1WgMdtND8NF7NDUuYmq8MpMj1NTCXkMDVG
The funds have since moved through hundreds of wallets. For screening, rely on your analytics provider’s tags, which follow the money several hops downstream.
