MakerDAO legacy keeper exploit: missing auth check
Published Oct 13, 2026
- Project
- Third-party MakerDAO keeper
- Chain
- Ethereum
- Date
- Oct 6, 2026
- Loss
- $542.98K
- Class
- Missing access control
- Basis
- Project post-mortem or audit firm analysis
In March 2020 a third-party keeper won four MakerDAO collateral auctions, 50 ETH each, and never settled them. The 200 ETH sat in the auction contract for six years, claimable by the keeper.
The keeper's own contract protected its privileged functions with ds-auth, except one: the function with selector 0x8804d1de. It accepted any address as an adapter, called Vat.hope to delegate the keeper's rights to it, then called join(), and never revoked the delegation with nope.
On October 6, 2026 an attacker passed in their own adapter. With the keeper's delegation it called deal() on the ETH-A Flipper for auctions 1457–1460, moved the 200 ETH to itself with flux(), and exited through GemJoin as 200 WETH, about $543K at the time. Maker's core contracts behaved as designed; the hole was entirely in the keeper.
The keeper's implementation is not verified on-chain, so the analysis rests on decompiled bytecode. The delegation to the malicious adapter is still in place.
How to protect your protocol
- Every privileged function needs its guard, checked as a list. Access control fails one function at a time. Keep a table of external functions and their required role, and make the build fail when a new function has no entry.
- Never accept an arbitrary address as something you delegate to. Adapters, routers and modules passed in by the caller must come from an allowlist.
- Revoke what you grant in the same call. A
hopewithout a matchingnopeleaves permanent delegation behind. Temporary permissions should be scoped to the transaction. - Sweep and retire old contracts. Funds left claimable for years are a standing bounty. When a bot or keeper is retired, withdraw what it can claim and remove its permissions.
- Verify your contracts on-chain. Unverified bytecode doesn't stop attackers, who decompile it anyway. It only stops reviewers.
Sources
- CertiK analysis
- Reconstructed proof of concept
- Attack transaction:
0xbb6940f7c2a1e68cafbae7bb9b94d09af9af06ec3a114f6996f2cab993f3a88c(block 26,131,471) - Keeper proxy
0x9c05a05893Ada984FC20D0DA0c046De5Cc0e8273, implementation0x68399ed8aa33C5b43F863EE6782de492006A5546(unverified), ETH-A Flipper0xd8a04F5412223F513DC55F839574430f5EC15531 - Loss in dollars is CertiK's figure at about $2,715 per ETH; other trackers report $538K.
How this was prepared: compiled from the sources above with the help of automated tools, then reviewed by the Foothold security team before publication.
Give Atlant a task
Could the same thing happen in your code?
Get it checked →