Root Cause
A MakerDAO ETH-A liquidation keeper proxy deployed during the "Black Thursday" crash of March 2020 was drained of 200 WETH (~$543K) on October 6, 2026. The keeper had participated in four ETH-A collateral auctions via MakerDAO's legacy Flipper contract — successfully calling bid() — but had never called deal() to claim the won collateral. This left 200 ETH sitting uncollected in the legacy auction contract for over six years.
CertiK identified a missing ds-auth guard on function selector 0x8804d1de in keeper implementation 0x68399ed8aa33C5b43F863EE6782de492006A5546. The function was a permissionless exit/redemption path:
// Vulnerable pattern — missing ds-auth (MakerDAO authorization framework)
// The `auth` modifier from ds-auth was omitted on this function:
function exit(address usr, uint wad) public /* MISSING: auth */ {
require(slip(ilk, address(this), -int(wad)));
gem.transfer(usr, wad);
}
// Correct pattern (should match other protected functions in the codebase):
function exit(address usr, uint wad) public auth {
require(slip(ilk, address(this), -int(wad)));
gem.transfer(usr, wad);
}
With auth absent, any address could call exit() and redirect the keeper proxy's accumulated collateral to an arbitrary destination. The attacker's wallet was funded via Tornado Cash.
Attack Steps
| Step | Action |
|---|---|
| 1 | Attacker wallet funded via Tornado Cash |
| 2 | Attacker calls permissionless exit() (fn 0x8804d1de) on keeper proxy, specifying their address as usr |
| 3 | Keeper proxy's 200 ETH collateral position in legacy Flipper is transferred to attacker |
| 4 | Attacker moves ETH out of protocol |
Impact
- Direct loss: 200 WETH ≈ $538,000–$543,000 USD
- Chain: Ethereum mainnet
- MakerDAO core contracts: Not affected — Vat, Liquidations 2.0 (Clip), and DAI stability were unimpacted
- Affected contract: Third-party keeper proxy (not MakerDAO Foundation code), implementation
0x68399ed8aa33C5b43F863EE6782de492006A5546 - Historical note: The collateral had been unclaimed since Black Thursday (March 12, 2020), when extreme volatility triggered mass liquidations. MakerDAO migrated to Liquidations 2.0 in 2021, making legacy Flipper bots largely obsolete.
Lessons for Auditors
- Apply
auth/onlyOwnerto every state-changing exit function. Functions that transfer tokens out of a contract must always carry an access-control modifier. In theds-authframework, omittingauthon a single function is equivalent to making it fully public — audit each function individually, not just the class. - Audit dormant contracts with on-chain balances. Keeper bots and protocol-adjacent contracts that are no longer actively maintained may hold real value. These should be included in periodic security reviews and balance sweeps.
- Enforce atomic deal() after bid(). Auction participant contracts should call
deal()in the same transaction asbid()(or trigger a keeper immediately after winning), to prevent unclaimed collateral accumulation. - Deprecation audits matter. When a protocol migrates to a new mechanism (e.g., Liquidations 2.0), legacy integration contracts should be identified, audited, and either decommissioned or swept of value.
- Monitor legacy contract balances. A simple on-chain monitor checking balances of known legacy contracts would have flagged 200 WETH sitting unclaimed for six years — an obvious target.