BadgerDAO Cloudflare Worker Injected Approval Drain (2021-12-02)
- Name: BadgerDAO Cloudflare Worker injected approval drain
- Date: 2021-12-02
- Protocol: BadgerDAO
- Chain: Ethereum
- Category: front-end compromise / approval phishing / ice phishing / wallet-permission drain
- Loss: about
2,076.54 BTCequivalent at the time of the exploit, roughly$116.3M-$120M; Badger later said some transferred assets were recoverable before final vault withdrawal - Summary: BadgerDAO’s audited vault contracts were not exploited directly. Instead, an attacker obtained unauthorized Cloudflare API access, used Cloudflare Workers to intermittently inject malicious JavaScript into the real Badger front end, and made users sign attacker-directed token approvals while they believed they were performing normal Badger actions. After quietly collecting approvals for days, the attacker used those permissions to drain user wallets and vault receipt tokens, including a roughly
900 byvWBTCtheft from one heavily impacted victim. - Trigger / entry point:
- Badger’s postmortem, as quoted in later case studies, said three Cloudflare accounts had been exposed to a signup / email-verification flaw that let unauthorized users create and view Global API keys before verification completed.
- Badger unknowingly completed one compromised account in mid-September 2021 and later used it for legitimate Cloudflare management activity, leaving the attacker with valid API access that Badger engineers did not know existed.
- On
2021-11-10, the attacker began using that API access to deploy Cloudflare Worker script injections intoapp.badger.com. - The injected script modified web3 transaction flows so users were prompted to approve an attacker-controlled spender while interacting with the genuine Badger interface.
- The first malicious onchain approval to the exploiter wallet appeared on
2021-11-20, but the mass draining wave did not begin until early2021-12-02.
- Root cause:
- The decisive failure was not in Badger’s Solidity. It was in the web-delivery control plane that prepared wallet actions for users.
- Badger’s front end and Cloudflare edge stack were trusted as if they were merely presentation infrastructure, but in practice they were authorization infrastructure because they determined what transactions wallets asked users to sign.
- Once the attacker could rewrite those prompts, the protocol’s audited contracts became irrelevant; users themselves granted the attacker spending authority.
- The preserved invariant is
the canonical Badger interface must never ask wallets to authorize a spender outside the intended protocol path. Cloudflare Worker compromise broke that invariant upstream of the chain.
- Exploit path:
- The attacker gained access to a Cloudflare API key associated with Badger’s account-creation / verification weakness.
- They used Cloudflare Workers to inject malicious JavaScript into the legitimate Badger application served from
app.badger.com. - The malicious code was applied intermittently, often for short periods, and selectively avoided some addresses, which helped it evade quick detection.
- When targeted users attempted normal Badger actions such as deposits or claims, the modified front end asked them to sign an attacker-directed
approve()orincreaseAllowance()style transaction. - Users signed because the domain, branding, and surrounding workflow were legitimate.
- After enough approvals accumulated, the attacker submitted draining transactions that used those live allowances to pull
wBTC, vault receipt tokens such asbyvWBTC, and other assets from victim wallets. - Badger paused contracts after the large drains were noticed, limiting some additional vault withdrawals but not reversing already granted approvals or non-vault token theft.
- What the attacker needed to be true:
- Cloudflare account / API access had to remain available long enough to keep injecting script changes into the genuine Badger front end.
- Wallet UX had to make the malicious spender approval look routine enough that users would sign it.
- Victims needed to hold transferable ERC-20s or Badger-related receipt tokens whose allowances could later be exercised.
- The malicious code needed enough stealth — selective targeting, intermittent activation, and frequent hash changes — to avoid being identified before a large approval base formed.
- Technical anatomy worth remembering:
- This was a wallet-drain incident without a smart-contract bug. From the chain’s point of view, valid users simply approved a spender and that spender later called
transferFrom(). - The attacker reportedly favored
increaseAllowance()-style prompts, which were easier to hide inside ordinary interaction flows than a raw obviously alarming approval screen. - The malicious script did not need to compromise every session. Periodic activation plus high-balance targeting was enough to quietly build a profitable approval inventory.
- The most important preserved lesson is that web infrastructure like Cloudflare Workers is part of protocol custody whenever the product depends on browser-constructed wallet transactions.
- This was a wallet-drain incident without a smart-contract bug. From the chain’s point of view, valid users simply approved a spender and that spender later called
- Representative transactions and addresses:
- Attacker spender / drain address:
0x1fcdb04d0c5364fbd92c73ca8af9baa72c269107 - Example victim approval transaction highlighted by rekt:
0x5e4c7966b0eaddaf63f1c89fc1c4c84812905ea79c6bee9d2ada2d2e5afe1f34 - Example major drain transaction highlighted by rekt:
0x951babdddbfbbba81bbbb7991a959d9815e80cc5d9418d10e692f41541029869 - Example heavily impacted victim address from that drain:
0x53461e4fddcc1385f1256ae24ce3505be664f249 - Approximate timeline markers preserved in Badger’s postmortem trail: injection activity beginning
2021-11-10, first malicious approval on2021-11-20, major drain wave starting around00:48 UTCon2021-12-02
- Attacker spender / drain address:
- Response / mitigation:
- Badger publicly acknowledged the incident and paused most vault activity within about 30 minutes of the community alert; older contracts and strategies without straightforward guardian access took longer.
- Badger rotated Cloudflare credentials, updated passwords and MFA, refreshed or deleted API keys where possible, and worked with Mandiant, Chainalysis, exchanges, and authorities.
- Because the exploit relied on user-granted approvals, the immediate user-side mitigation was approval revocation rather than waiting for a contract patch.
- Badger later reported that recoverable assets still sitting in vaults were returned to affected wallets and that roughly 40% of affected users benefited from that recovery path.
Pattern tags
- frontend-compromise-approval-phishing
- Same real domain, different edge-served bytes:
app.badger.comstayed canonical while Cloudflare Worker authority rewrote signer-facing JavaScript at the edge - User-signed allowance capture, not contract breakage: the draining primitive was still ordinary
approve/increaseAllowancestyle wallet authority later cashed out throughtransferFrom() - Intermittent high-value targeting turned a brief edge compromise into an approval inventory-building campaign before the drain wave
- Vault pause and guardian controls were only partial brakes because already granted ERC-20 and vault-token allowances lived in user wallets, not inside Badger’s paused contract logic
Why the exploit worked
- Badger’s strongest user trust signal — the real
app.badger.comdomain and normal interaction flow — remained intact while the signer-facing bytes changed underneath it. That let the attacker harvest approvals without needing victims to leave the canonical site. - Cloudflare Worker deployment authority was effectively wallet-prompt authority. Once the attacker could inject JavaScript into the edge path, they controlled which spender address the wallet was asked to trust, which mattered more than Badger’s audited Solidity.
- The attacker did not need to steal funds immediately. Intermittent activation and selective targeting let them quietly accumulate durable allowances for days, then monetize those approvals later in a concentrated drain wave.
- Incident response on the protocol side had an asymmetry problem: pausing vaults could slow some follow-on withdrawals, but it could not retroactively revoke approvals users had already granted from their own wallets.
- The decisive lesson is therefore not
front end looked sketchybutone genuine production web-delivery surface had become signer-control infrastructure without being defended like signer-control infrastructure.
Three signer-authority promotions this incident collapsed
-
A canonical signer-facing route still existed
- Users were still interacting with the real
app.badger.cominterface, so the weakest trust claim remained true:I am on the genuine Badger site I normally use for this action. - That preserved domain-level familiarity is what made the later approvals blend into an otherwise legitimate workflow.
- Users were still interacting with the real
-
Those edge-served bytes were still treated as canonical Badger prompt truth
- Once the attacker could use Cloudflare Worker authority to rewrite Badger’s live browser path, the signer-facing JavaScript was no longer the same thing as
the intended Badger wallet-construction runtimeeven though the site and product context still looked authentic. - The dangerous promotion was from
this is the real Badger domaintothe spender this page is asking me to trust must still be Badger's intended spender for this exact action.
- Once the attacker could use Cloudflare Worker authority to rewrite Badger’s live browser path, the signer-facing JavaScript was no longer the same thing as
-
Those accepted prompts still inherited durable wallet authority after discovery
- Users did not merely view malicious content; they granted live ERC-20 and vault-token allowances that the attacker could monetize later through
transferFrom(). - That is why the incident had a long asymmetry: Badger could pause contracts and stop fresh prompts, but already harvested approvals remained valid until users revoked them or the attacker exhausted them.
- Users did not merely view malicious content; they granted live ERC-20 and vault-token allowances that the attacker could monetize later through
Detection and audit pivots
- Inventory every authority that could have changed the bytes served from
app.badger.com: Cloudflare account recovery, Global API keys, Worker deployment rights, DNS, CI/CD, feature flags, and any wallet-critical remote config. - Monitor for bursts of approvals from the canonical domain to EOAs or previously unseen spender contracts, especially when the approval inventory forms before any drain transactions appear.
- Treat intermittent or high-balance-selective signer-prompt drift as an expected attacker evasion tactic. Short activation windows and changing worker payload hashes should raise suspicion rather than lower it.
- Ask whether the protocol can independently verify and alert on the spender addresses its live UI is currently presenting, rather than assuming the canonical domain itself is sufficient evidence.
- Test whether emergency response can do two things at once: stop new signer prompts quickly and push allowance-revocation guidance fast enough that already harvested approvals do not stay live for hours or days.
Failure-prevention checklist
- Treat signer-facing web delivery as custody infrastructure. Cloudflare account recovery, Global API keys, Worker deploy rights, DNS, CI/CD, and cache-purge authority should sit behind the same offboarding, MFA, review, and alerting rigor as treasury-signing or admin-change paths.
- Keep wallet-critical routes smaller and more static than the rest of the product. If
app.badger.commust ask for approvals, minimize mutable edge logic, third-party scripts, and runtime rewrites on that route. - Pin or machine-verify the spender addresses the live UI is allowed to present. A wallet-critical page should not be able to silently swap Badger’s intended spender for an arbitrary EOA without tripping an integrity control or hard allowlist failure.
- Prefer exact-amount approvals, bounded permits, and short-lived signer authority over durable unlimited allowances, especially for vault receipt tokens and high-value BTC wrappers.
- Monitor approval inventory, not only drains. Bursts of many wallets approving one fresh EOA or previously unseen spender from the canonical domain should page responders before
transferFrom()monetization starts. - Build emergency response around the asymmetry this incident exposed: stopping new prompts and revoking already granted authority are different jobs. In-product warnings, spender-specific revoke guidance, and wallet-partner alerts should already be prepared.
- Keep per-asset and per-route blast-radius limits even after a bad approval lands. If fresh allowances still need a Badger vault withdrawal or redeem path to become final user loss, pause controls should be narrow, fast, and rehearsed.
- Reconcile live UI spender inventory and onchain approval telemetry continuously so
real domain, wrong spenderbecomes detectable within minutes instead of after a drain wave.
Audit prompts
- Which exact systems could have changed the JavaScript or HTML a Badger user received on
app.badger.comduring the incident window, and which of those systems still required manual offboarding or key cleanup? - Can any Cloudflare Worker, edge rule, remote config blob, analytics tag, or wallet helper mutate spender selection or typed approval contents after the reviewed frontend bundle is built?
- What independent control proves that the spender currently shown by the live UI is one of Badger’s intended protocol destinations rather than a fresh EOA or unknown contract?
- If a signer-facing route is compromised for one hour, what is the largest authority a user can be tricked into granting today: unlimited ERC-20 approval, vault-token approval, Permit2 blanket permission, or something broader?
- Would responders notice the approval campaign before the drain campaign? What alert fires on
many wallets approved one attacker spender from the canonical siterather than waiting forlarge transferFrom()clusters? - Once a malicious prompt is suspected, how quickly can Badger both stop new signatures and push exact revoke instructions to already exposed users?
- Which assets or vault paths remain monetizable even after Badger pauses contracts, and where are the remaining post-approval blast-radius controls enforced?
- Does the protocol have a trustworthy fallback path or degraded-mode interface that can keep user read access while signer-critical routes are frozen, or does incident response still force an all-or-nothing frontend decision?
Lessons that should already be standard
- The real domain is not enough. If Cloudflare edge logic can rewrite signer-facing JavaScript, then
app.badger.comauthenticity is only as strong as that edge-control surface. - Web-delivery systems that can shape wallet prompts are not support infrastructure; they are delegated authorization infrastructure.
- Unlimited allowances turn short-lived frontend compromise into long-lived theft rights. The compromise window and the drain window do not have to be the same event.
- Protocol pause buttons are only partial containment when the dangerous authority already lives in user wallets.
Contracts pauseddoes not meanspender rights revoked. - Approval telemetry is incident telemetry. In wallet-drainer cases, the first real exploit transaction is often the approval, not the later cash-out.
- High-value users do not have to be targeted continuously. Selective, intermittent prompt injection is enough to build a profitable approval inventory while looking like noise.
- Offboarding mistakes on external operational surfaces can become signer-authority failures even when internal code repositories and contracts remain untouched.
- A front end that cannot prove which spender it is asking users to trust is already too mutable for custody-critical use.
Why this incident matters
- It is one of the clearest large-scale examples of a DeFi protocol losing nine figures through web infrastructure compromise rather than through a contract bug.
- It preserves the
real domain, real app, malicious wallet promptbranch of approval abuse, which is analytically different from router arbitrary-call drains or callback spoofing. - It is also a clean exemplar for canonical-domain runtime-byte integrity: the real failure was that
app.badger.comno longer proved that Cloudflare edge behavior, HTML bootstrap, and delivered JavaScript still belonged to the intended wallet-construction surface. - It is a useful same-domain companion for third-party runtime allowance and integrity pinning too: once a Cloudflare Worker, edge HTML rewrite surface, or similarly external runtime hook can inject wallet-facing JavaScript on the real signing route, that surface is no longer
just delivery plumbingorjust web ops— it is signer-surface execution authority. - It is also a useful
where this note stopscontrast for the release-integrity branch. Badger is not thesame real site, different loader-resolved bytescousin preserved by Ledger Connect Kit, and it is not thesame familiar runtime URL, different operator-resolved bytescousin preserved by Polyfill.io. The real site stayed Badger’s; the signer-facing drift happened inside Badger’s own Cloudflare-controlled edge path. - It also preserves the routing boundary against software supply-chain compromise and release-provenance failure: the decisive compromise sat in Badger’s own edge-delivery stack, not in a shared upstream package ecosystem or in a broader
reviewed source != executed artifactrelease-chain drift story spanning many downstream apps. - It forces a sharper control-surface view: CDN, DNS, front-end build controls, and any edge script-injection path can be just as custody-relevant as multisigs and proxy admins when user wallets rely on them to construct transactions.
Internal linkages
- Core recurring pattern: frontend-compromise-approval-phishing.
- Positive-design companion: canonical-domain-runtime-byte-integrity — the narrower recovery and architecture question here is what the canonical Badger domain should have proved about Cloudflare worker behavior, HTML bootstrap integrity, and the exact browser-fetched runtime bytes before users trusted any wallet prompt.
- Narrower browser-policy companion: third-party-runtime-allowance-and-integrity-pinning — Badger shows that a wallet-critical route can stay on the right domain yet still be unsafe if edge-managed runtime injection surfaces are allowed to alter signer-facing JavaScript without being treated like privileged execution rights.
- Adjacent pattern: arbitrary-call-token-approval-drain — both monetize token approvals, but Badger captured the approval offchain through the interface rather than abusing a buggy onchain router.
- Boundary note: software-supply-chain-compromise and release-provenance-failure — useful contrasts for the
real domain, wrong bytesoutcome family, but Badger stays on the single-site edge-compromise branch because the compromised authority was Badger’s own Cloudflare path rather than thesame real site, different loader-resolved bytesshared-loader branch preserved by Ledger Connect Kit or thesame familiar runtime URL, different operator-resolved bytesthird-party-runtime branch preserved by Polyfill.io.
Control surface
-
The decisive leverage sat in Badger’s Cloudflare account hygiene, worker-deployment authority, front-end integrity monitoring, HTML / script delivery path, any edge-managed runtime injection rights on wallet-critical routes, and the wallet prompts users were trained to trust on the canonical domain.
-
Once the front end could ask for attacker-chosen approvals, the protocol’s audited contracts no longer defined the real security boundary.
-
The preserved design question is not only
how do we stop front-end compromise?buthow do we prove that the real Badger domain is still serving the intended wallet-construction bytes through its edge path right now?. -
Sources:
- https://rekt.news/badger-rekt
- https://www.coindesk.com/business/2021/12/10/badgerdao-reveals-details-of-how-it-was-hacked-for-120m/
- https://forta.org/blog/how-to-derail-a-120-million-dollar-hack
- https://www.microsoft.com/en-us/security/blog/2022/02/16/ice-phishing-on-the-blockchain/
- https://www.quadrigainitiative.com/casestudy/badgerdaomaliciouscodeinjected.php
-
Last reviewed: 2026-08-01 UTC