Defensive pattern
Guarded delegation pattern (illustrative)
Shows the three controls that would have stopped this incident: an authority check on the function, an allow-list for the adapter it may call, and revocation after use. Illustrative, not compiled, not audited.
Code
Written by ThreatWire to illustrate the fix. Not compiled or audited, and not the affected project's code.
1// Illustrative defensive pattern written by ThreatWire. It is NOT the keeper's code and has not2// been compiled or audited. It shows the shape of the fix described in the incident record.4interface IVat {5 function hope(address usr) external;6 function nope(address usr) external;7}9contract GuardedKeeper {10 IVat public immutable vat;11 address public owner;12 mapping(address => bool) public approvedAdapter;14 modifier auth() {15 require(msg.sender == owner, "unauthorized");16 _;17 }19 constructor(IVat _vat) {20 vat = _vat;21 owner = msg.sender;22 }24 function setAdapter(address adapter, bool ok) external auth {25 approvedAdapter[adapter] = ok;26 }28 // A function that grants rights over this contract's Vat account, or calls back into a29 // supplied address, is an authority transfer. It needs the same gate as every other30 // privileged function, an allow-list for the target, and a revocation once the work is done.31 function runWith(address adapter) external auth {32 require(approvedAdapter[adapter], "adapter not approved");33 vat.hope(adapter);34 // ... perform the intended work with the adapter ...35 vat.nope(adapter);36 }37}Components and what they do
- auth modifier
- approvedAdapter allow-list
- hope then nope around the callback
Fixes and defensive tests
- Authority check on every function that grants rights or calls out to a supplied address.
- Allow-list the counterparties a keeper may delegate to.
- Revoke delegations in the same call.
Contracts and transactions from the incident
- 0x01EB957E…BdA5FA Attacker wallet (externally owned)
- 0xEc997d2a…321dcF Attack helper contract
- 0xf09a1307…DCC170 Adapter contract holding rights over the keeper's Vat account
- 0x9c05a058…0e8273 Dormant ETH-A keeper (EIP-1967 proxy), affected
- 0x68399ed8…6A5546 Keeper implementation (source reported unverified)
- 0xd8a04F54…C15531 MakerDAO ETH-A Flipper (retired auction house)
- 0x35D1b3F3…A0492B MakerDAO Vat (core accounting)
- 0x2F0b23f5…6B042A MakerDAO ETH-A GemJoin (collateral exit)
- 0xC02aaA39…756Cc2 Wrapped Ether (WETH)
- 0xd90e2f92…24F31b Mixer router that received the proceeds
- 0xaDC374E7…DaF073 Keeper owner and 2020 operator
- 0xbb6940f7…f3a88c exploit
- 0x4a587af4…ad1167 attack setup (helper deployment)
- 0xd4c0b18a…048ac6 attacker funding
- 0xb8ecff9c…ebefeb proceeds (first mixer deposit)
Related files
1 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/2026-10/MakerDAOFlipKeeper_exp.solSame incident · 215 lines
File record
- Name
- Guarded delegation pattern (illustrative)
- Author
- ThreatWire editorial
- Language
- Solidity
- Size
- 37 lines
- Date
- 2026-10-19
- Licence
- ThreatWire editorial text; all rights reserved
- Retrieved
- 2026-10-19
- Incident
- MakerDAO flip keeper: an unprotected function handed an attacker control of dormant auction winnings
- Protocols
- Third-party ETH-A flip keeper
- Chains
- Ethereum
Provenance
Retrieved 2026-10-19. Original text written by ThreatWire. ThreatWire has never executed this file.