BlackHartBlackHart

Fluid

May 27, 2026·Ethereum·Key Compromise
$225K
total loss
StatusConfirmed
View current Fluidscore →

An attacker compromised the operational keys that propose and approve Fluid's reward payout lists, then used them to approve self-serving reward lists and claim with empty proofs across three chains. In total about 125,109 FLUID and 51,946 GHO, plus a little cbBTC (about $225,000), were taken from Fluid's reward distributors on Ethereum, Base, and Arbitrum. Fluid's lending markets, vaults, DEX, and user deposits were not affected. The Layer 2 proceeds were bridged back to Ethereum, swapped for ether, and about 142.6 ETH was routed into Tornado Cash. Fluid later removed the compromised keys and moved the remaining reward funds to safety. A separate, much larger movement of roughly 70 to 110 million dollars out of Fluid in the days after was depositors withdrawing their own funds, a confidence driven bank run, not a second hack.

User depositssafe
Lending marketssafe
Vaultssafe
DEX liquiditysafe
Reward distributorsdrained(about 112,900 FLUID, 47,900 GHO and a little cbBTC claimed via a fake reward list, roughly 215,000 dollars)
Remaining reward fundspartially affected(about 314,000 FLUID and 7,400 USDC swept to safety by the team)
What the score saw

Our pre-hack assessment of Fluid flagged operational security as its single weakest area, the lowest-scoring layer in the breakdown. The exact failure mode, privileged operational keys that could be turned into a payout without a second independent custodian or a waiting period, was already what our score was warning about. The exploit confirmed that warning rather than contradicting it.

Exploit anatomy

The attacker controlled Fluid's compromised reward proposer key and approver key, approved a fake reward list, then claimed from the FLUID distributor and GHO distributor into their wallet, swapped the tokens for ether, and routed the proceeds into Tornado Cash.

FUND FLOWROOT CAUSE / ENABLERS
Stage 1 · ROOT CONTROL
FLUID distributor
112,883.85 FLUID
0x7060fe0d...d163b0
~ $167K · dominant
GHO distributor
47,903.67 GHO
0xf398e66b...cc7714
~ $48K
cbBTC distributor
0.0068 cbBTC
0x9901dc63...191c4f
~ $0.5K · thin
Base + Arbitrum
+12,225 FLUID, +4,042 GHO
Base & Arbitrum distributors
Proposer key
0x4f1047...3a83
key compromise
Approver key
0x85dc44...97b2
key compromise
Two-person control collapses to one
single owner held both keys, proposed and approved a self-serving root
Stage 2 · CLAIM
Attacker wallet
proof = [ ] · 125,108.86 FLUID + 51,945.93 GHO
0x4925120c...1d3dfb
$225K cross-chain claim, +24s
Empty-proof claim, no timelock
single-leaf root equals its only leaf so proof=[] verifies; nothing delayed claim after approval
Stage 3 · SWAP
MetaMask swap router
to ~ 143 ETH total
0x881d4023...8d300c
ETH + bridged L2 loot, MetaSwap
No payout velocity cap
no per-cycle or per-claim limit bounded the outflow
missing control
Stage 4 · MIXER
Tornado Cash router
~ 142.6 ETH in
0xd90e2f92...24f31b
L2 loot bridged to ETH, then mixed
No realtime outflow alerting
~10h to detect, remove the keys, and sweep remaining funds
missing control
Untouched

Safe. User deposits, lending markets, vaults, and DEX liquidity were not affected.

Mechanism

Both reward signers were key-compromised. The attacker called proposeRoot then approveRoot on the distributors across Ethereum, Base and Arbitrum, set self-serving roots, and claimed with empty proofs. No contract was broken.

Source
blackhart.io/hacks/fluid-merkle-distributor-key-compromise
verified on-chainL2 proceeds bridged to Ethereum (MetaBridge, Across, Relay), about 142.6 ETH to Tornado Cash, no CEX
Full forensic detail

Step-by-step reconstruction, root cause, counterfactuals, remediation, and disclosure timeline.

Exploit anatomy

1.
The attacker gained control of two Fluid operational signing keys: the reward-root proposer (0x4f104710f8d9f6efb28c4b2f057554928daa3a83) and the reward-root approver (0x85dc44e0c3afdfedca52678bd4c000917c6597b2). Both had been single-purpose operational signers since September 2024. The exfiltration vector is not publicly confirmed; it is consistent with a leaked deployment or signing secret.
2.
Using the proposer key, the attacker submitted a self-serving reward root to the FLUID reward distributor at 2026-05-27 21:11:11 UTC.
0x7060fe0d...d163b0proposeRoot(bytes32,bytes32,uint40,uint40,uint40)tx:0x9160e1ee...23e084
3.
Using the approver key, the attacker approved that same root 12 seconds later, at 21:11:23 UTC, satisfying the two-step control with a single owner.
0x7060fe0d...d163b0approveRoot(bytes32,bytes32,uint40,uint40,uint40)tx:0x2a47de0f...9b9eaf
4.
12 seconds after that, at 21:11:35 UTC (24 seconds after the proposal), the attacker claimed 112,883.85 FLUID with an empty Merkle proof and an empty signature. The proof can be empty because a single-entry list has a root equal to its only leaf, so no proof path is needed. The verifier behaved correctly.
0x7060fe0d...d163b0claim(address,uint256,uint8,bytes32,uint256,bytes32[],bytes)tx:0x6f47dd16...3b0e94
5.
The attacker repeated the same propose, approve, claim pattern against the GHO reward distributor, taking 47,903.67 GHO at 21:13:59 UTC, and against a third distributor for a small amount of cbBTC at 22:57:35 UTC.
6.
The attacker swapped the stolen FLUID, GHO and cbBTC for about 103 ether through the MetaMask swap router, then routed roughly 140 ether into the Tornado Cash mixer, partly through two relay wallets and partly by direct deposit.
7.
About ten hours after the first theft, on 2026-05-28 at 07:05 UTC, Fluid removed the compromised proposer and approver roles from ten reward distributors in a single batched transaction, then swept roughly 314,000 FLUID and 7,400 USDC of remaining reward balances to a safe address, preventing a much larger loss.

Root cause

Fluid's reward system uses a two-key control on Ethereum: a proposer key submits a Merkle root of reward allocations, and a separate approver key approves it before users can claim. Both of these were dedicated single-purpose operational signers active since September 2024 (proposer 0x4f104710, approver 0x85dc44e0). The attacker obtained both private keys and therefore controlled both halves of the two-step control. They proposed a single-leaf root paying themselves, approved it with the second key, and claimed with an empty Merkle proof (valid because root equals leaf for a one-entry tree) and empty signature. No smart contract was bypassed, no Merkle verification was broken, no oracle or governance was manipulated. The failure is operational key custody: privileged reward-approval keys were held in a way that allowed a single compromise to take both roles, and there was no timelock between approval and claim to give defenders a reaction window. The drained contracts are reward distributors only; core Fluid lending markets, vaults, and DEX liquidity were never in scope of these keys.

Prevention analysis

Add a timelock between approving a reward root and claiming against it.

A delay of even a few hours between approval and payout would have turned a 24-second smash-and-grab into a detectable event with a reaction window. The team caught the compromise within ten hours, so a timelock alone would likely have prevented most of the loss.

Hold the proposer and approver keys in separate multisig or threshold (MPC) custody.

A single key compromise becomes insufficient. The attack needed both keys at once; independent threshold custody on each role removes the single-extraction path entirely.

Add an on-chain per-cycle payout limit on reward distributors.

A cap on how much a single claim or cycle can release would have bounded the loss well below the full reward balance, regardless of who held the keys.

Similar incidents

Polymarket

Operational private-key compromise enabling a unilateral drain of protocol-adjacent wallets. Same root cause: privileged operational keys held without multisig or threshold custody.

Drift Protocol

Operational key compromise (social engineering plus blind signing) enabling unilateral control of privileged actions. Same class: weak operational key security on a privileged role.

Remediation

1.Rotate every operational signing key with proposer or approver authority, and audit the infrastructure that held them (deploy pipelines, signing services, stored secrets) for further exposure.
2.Move the proposer and approver roles to independent multisig or threshold (MPC) custody so that no single compromise can control both halves of the reward approval.
3.Add a timelock between approving a reward root and allowing claims, giving defenders a window to react to a malicious root.
4.Add an on-chain payout-velocity limit on reward distributors and real-time alerting on abnormal claim activity.
5.Publish a public post-mortem that states plainly that operational keys were compromised, what was lost, and what has changed. As of this writing Fluid has told users only that reward claiming is paused for updates, without disclosing the key compromise or the loss.

Timeline

2026-05-27Compromised proposer key submits a self-serving reward root to the FLUID distributor.
2026-05-27Compromised approver key approves the root 12 seconds later.
2026-05-27Attacker claims 112,883.85 FLUID with an empty proof, 24 seconds after the proposal.
2026-05-27Attacker claims 47,903.67 GHO from the GHO distributor using the same pattern.
2026-05-27Attacker swaps the stolen tokens for ether through the MetaMask swap router.
2026-05-27Attacker claims a small amount of cbBTC from a third distributor.
2026-05-28Attacker begins depositing proceeds into the Tornado Cash mixer; roughly 140 ether is routed in over the next hours through two relay wallets and direct deposits.
2026-05-28Fluid removes the compromised proposer and approver roles from ten reward distributors in one batched transaction.
2026-05-28Fluid sweeps roughly 314,000 FLUID and 7,400 USDC of remaining reward balances to a safe address.
Continuous adversarial monitoring

Get your protocol scored across 12 dimensions, or request ongoing coverage.