Root Cause
During the March 2020 Black Thursday event, MakerDAO's collateral auction system cleared positions with zero-bid submissions due to extreme network congestion. Some keeper bots accumulated ETH collateral in the resulting auction contracts but never settled their final claims — leaving 200 ETH split across four lots of 50 ETH each sitting in a legacy keeper implementation contract for over six years.
CertiK identified that the keeper's implementation contract contained a function with selector 0x8804d1de that lacked any access control:
// Simplified pseudocode of the vulnerable function
// selector: 0x8804d1de — no access modifier, no require(msg.sender == owner)
function withdrawUnsettledCollateral(address recipient, uint256 amount) external {
// No access check — any caller can specify recipient and amount
WETH.transfer(recipient, amount);
}
The function appears to be a delegation/recovery helper added for keeper management, but it was never restricted to authorized callers. Because the contract is a legacy deployment from the MakerDAO V1 era (predating Sky's rebranding), it received no ongoing security attention.
Attack Steps
| Step | Action |
|---|---|
| 1 | Attacker identifies legacy MakerDAO ETH-A keeper implementation contract holding unclaimed 200 ETH |
| 2 | Calls the unguarded function 0x8804d1de with attacker's address as recipient |
| 3 | Contract transfers 200 ETH in four 50 ETH tranches (no resistance, no timelock) |
| 4 | Attacker routes proceeds through Tornado Cash in 10 ETH batches to obscure trail |
Impact
- Loss: 200 ETH (~$542K at time of exploit; some outlets initially reported $5M — this appears to be an error)
- Chain: Ethereum mainnet
- Affected: Legacy MakerDAO ETH-A auction keeper contract from March 2020
- Sky / MakerDAO V2 core: Not directly affected; funds were stranded in an old, peripheral contract
- Recovery: No official recovery or post-mortem from Sky/MakerDAO had been announced as of reporting
Lessons for Auditors
- Legacy contracts holding value are live attack surface. A contract that is no longer actively used is not safe — if it holds funds and is reachable on-chain, it is still exploitable. Conduct periodic sweeps of dormant contracts for unclaimed assets and upgrade or migrate them before they become targets.
- Zero-bid auction mechanics create orphaned funds. Any auction system that can close with a zero bid (or a below-floor bid during congestion) should include a mandatory settlement timeout that automatically routes proceeds to a safe destination after a grace period.
- Function-level access control audits must cover every external function. The
0x8804d1deselector was almost certainly present in the source code but missed in a review because the contract was considered "inactive." Auditors should enumerate all external/public functions in every deployed contract, including old versions. - Tornado Cash post-exploitation pattern. The attacker's immediate use of Tornado Cash in fixed-size batches (10 ETH) is a signature laundering technique. Monitoring for this pattern can provide early warning for incident responders.