The BadgerDAO Frontend Attack: $120 Million Lost to Malicious Approvals

The smart contracts were fine. The audits were clean. The blockchain code did exactly what it was supposed to do. On December 2, 2021, BadgerDAO still lost $120 million—because the attack happened in a browser, not on-chain.

The Setup

BadgerDAO was a DeFi protocol that let users deposit Bitcoin-backed tokens (primarily WBTC) into yield-generating vaults on Ethereum. It had a reasonable security posture for its smart contracts: audited code, multisig governance, the usual precautions.

What it had in common with nearly every other DeFi protocol was a standard web architecture: a centralized frontend hosted on a CDN, loading JavaScript files that constructed transaction data, which users signed in MetaMask. The frontend was the interface between users and the blockchain. Compromise the frontend, and you control what users sign.

In early November 2021, attackers obtained a Cloudflare API key for BadgerDAO’s account. Cloudflare’s Workers product lets you inject JavaScript into any page request. With a valid API key, the attackers could modify every page served from app.badger.finance—silently, for every visitor, without touching the underlying hosting infrastructure.

What the Script Did

The injected code intercepted MetaMask transaction requests. When a user initiated any transaction with BadgerDAO—depositing, staking, claiming rewards—the script inserted an additional step before the intended action: an ERC-20 approval granting the attacker’s address the ability to spend the user’s tokens up to type(uint256).max.

From a user’s perspective, they were doing one thing. MetaMask would prompt them to sign two transactions instead of one, but the approval request looked like a normal protocol interaction. The target address in the approval wasn’t obviously an attacker’s wallet—it was just a contract address.

Many users clicked through without scrutinizing the details. The script was selective: it only injected the malicious approval when the user had significant token balances, avoiding small wallets that would generate noise without meaningful return.

Three Weeks of Patient Waiting

The script went live around November 10. The attackers didn’t immediately drain the wallets they’d compromised. They collected approvals for three weeks.

Some users noticed something was off. Forum posts and Discord messages from late November described unexpected approval requests for unrecognized addresses. These reports reached the BadgerDAO team. They were not treated as an emergency.

On December 1–2, with hundreds of wallets holding unlimited approvals to their address, the attackers began executing transferFrom calls—pulling every approved token from every compromised wallet in rapid succession.

Celsius Network, which used BadgerDAO vaults for yield, lost approximately $51 million—about 42% of the total stolen. They had interacted with the compromised frontend without realizing the approvals they were granting were malicious.

The total loss across all victims was approximately $120 million in WBTC, ibBTC, and related tokens.

The Approval Mechanism and Why It Matters

ERC-20 approvals are a fundamental primitive in DeFi. When you want to let a protocol move your tokens, you first grant it an allowance:

// Grant protocol permission to spend up to amount of your tokens
token.approve(protocolAddress, amount);

// Protocol later moves tokens on your behalf
token.transferFrom(you, protocol, amount);

Many DeFi frontends request “unlimited” approvals (type(uint256).max) so users don’t have to re-approve on every interaction. Once granted, these approvals persist until explicitly revoked. The approved address can call transferFrom at any time—days, months, or years later—as long as the approval remains active.

The BadgerDAO attackers exploited this persistence. They built up a library of unlimited approvals over three weeks, then exercised them all at once. Even after the attack was discovered and the frontend was taken down, the approvals remained valid on-chain until users manually revoked them.

The Twelve-Day Window

The gap between the first user reports of suspicious approvals (around November 20) and the mass drain (December 2) was twelve days. During that window, more users visited the site and were compromised. Each day the script ran was another day of accumulated exposure.

The incident response failure here is worth being direct about: user reports of anomalous approval requests are a high-signal warning. A single report might be a confused user. Multiple reports describing the same unexpected address as a recipient of unlimited approvals is a different matter. Treating it as such on November 20 would not have prevented all the losses, but it would have constrained them substantially.

BadgerDAO paused all vault contracts when the drain was detected on December 2. The pause was effective for preventing further protocol-level harm, but the approvals that had already been granted could still be exercised against users’ wallets outside of the protocol’s control.

How This Happens Again

The BadgerDAO attack vector is not exotic. Cloudflare Workers, Cloudflare Pages, Vercel, Netlify, AWS CloudFront—every CDN layer that can inject or modify served content is a potential attack surface if those credentials are compromised. API keys, OAuth tokens, and deployment credentials are common targets for phishing and credential stuffing.

A few measures that reduce this exposure:

Subresource Integrity (SRI) hashes JavaScript file contents and causes browsers to reject any file that doesn’t match the expected hash. This prevents CDN-level injection of modified scripts—if the attacker modifies app.js, the browser refuses to execute it.

Content Security Policy (CSP) can restrict which scripts the browser will run at all, and which external connections the page can make. A tight CSP won’t prevent all attacks but eliminates whole categories of injection.

Transaction simulation shows users exactly what a transaction will do before they sign it. Tools like Pocket Universe or Fire display the full state changes—token transfers, approval grants, balance changes—in plain language. A user who sees “Grant 0x1FCd… unlimited access to all your WBTC” before signing has the information to refuse.

Approval limits: Frontends requesting exact amounts rather than unlimited allowances limit the blast radius when approvals are abused. EIP-2612 permit signatures, which expire and are single-use, are a better model than persistent on-chain approvals for most interactions.

The underlying problem—that the frontend constructs the transactions users sign, making the frontend a critical security boundary—is structural. The user’s wallet has no way to distinguish “this approval request came from a legitimate BadgerDAO frontend” from “this approval request came from a script an attacker injected into the BadgerDAO frontend.” The browser doesn’t know. MetaMask doesn’t know. Only the user can verify, and that requires reading the transaction details carefully every time.


References