During the past week (2026/10/05 - 2026/10/11), we observed 1 security incident with a total estimated loss of approximately $542K.
| Date | Incident | Type | Estimated Loss |
|---|---|---|---|
| 2026/10/06 | MakerDaoBot | Lack of Access Control | ~$542K |
Weekly Highlight: MakerDaoBot
On October 6, 2026, an attacker exploited a lack of access control in MakerDaoBot, a legacy MakerDAO liquidation bot on Ethereum, and withdrew 200 ETH, worth approximately $542K. The vulnerable function let any caller supply an external contract, grant it control over the bot's balances, and execute its code. The attacker used this path to settle four old collateral auctions and transfer the released ETH in a single transaction.
Background
MakerDaoBot participated in MakerDAO's legacy ETH-A collateral auctions through Flipper, the collateral-auction contract used by the older liquidation system. The bot managed Vat permissions, deposited and withdrew ETH and DAI, and processed liquidation tasks.
The Vat is MakerDAO's internal ledger. It records each address's free collateral by collateral type in gem[ilk][address] and its internal DAI credit in dai[address]; there is no separate wallet or account contract. MakerDaoBot's Vat balances therefore mean the ledger entries recorded under the bot contract's own address.
Calling hope(usr) does not transfer those balances. It sets can[msg.sender][usr] to 1, allowing usr to pass Vat's wish() permission check and operate balances recorded under msg.sender. Functions such as flux() and move() use this check when transferring internal collateral or DAI.
mapping(address => mapping (address => uint)) public can;
function hope(address usr) external note { can[msg.sender][usr] = 1; }
function nope(address usr) external note { can[msg.sender][usr] = 0; }
function wish(address bit, address usr) internal view returns (bool) {
return either(bit == usr, can[bit][usr] == 1);
}
mapping (bytes32 => mapping (address => uint)) public gem; // [wad]
mapping (address => uint256) public dai; // [rad]
function flux(bytes32 ilk, address src, address dst, uint256 wad) external note {
require(wish(src, msg.sender), "Vat/not-allowed");
gem[ilk][src] = sub(gem[ilk][src], wad);
gem[ilk][dst] = add(gem[ilk][dst], wad);
}
function move(address src, address dst, uint256 rad) external note {
require(wish(src, msg.sender), "Vat/not-allowed");
dai[src] = sub(dai[src], rad);
dai[dst] = add(dai[dst], rad);
}
Vulnerability Analysis
The root cause was a lack of access control on function 0x8804d1de() in the MakerDaoBot contract at 0x6839...5546. The function treated varg0 as an external contract address (referred to below as the adapter), calling drip(), vat(), and later join() on that address. After obtaining the Vat address through vat(), the bot checked whether the adapter could operate its Vat balances. If not, it called hope(varg0) to grant permission and then invoked join() on the same adapter.

Other privileged entry points in the same contract used a ds-auth check before executing their operations. The comparison below shows two guarded entry points passing a bytes4 selector and msg.sender into the authorization helper, then reverting with ds-auth-unauthorized when the check fails. Function 0x8804d1de() had no equivalent caller check.

Function 0x8804d1de() therefore let any caller choose the address that received Vat permission. The bot granted that address permission through hope() and immediately called its join() function. The delegation remained active during this external call, allowing code at the chosen address to operate the bot's internal collateral and DAI balances.
Attack Analysis
The following analysis is based on the transaction 0xbb6940...f3a88c.
- Step 1: The attacker's EOA called an attack contract it had deployed. That contract deployed a malicious adapter, then called MakerDaoBot's
0x8804d1de()function with the adapter address. The bot found no existing delegation in Vat'scanmapping and calledhope(), allowing the adapter to operate the balances recorded under the bot's address.

- Step 2: MakerDaoBot called the adapter's
join()function. While the delegation was active, the malicious code calleddeal(uint256 id)on the retiredETH-AFlipper and settled auctions 1457, 1458, 1459, and 1460. The adapter then used its Vat permission to move 200 units ofETH-Acollateral from MakerDaoBot to itself, exited through ETH Join to receive 200WETH, and transferred the tokens to the attack contract.

- Step 3: The attack contract unwrapped 200
WETHand sent 200ETHto the attacker's EOA, worth approximately $542K at the time.

Conclusion
The root cause was a lack of access control in MakerDaoBot, not a flaw in MakerDAO's Vat permission system. A public function let any caller choose an external contract, grant it control over the bot's internal balances, and execute its code. The attacker used this path to settle four legacy auctions and withdraw 200 ETH.
Any function that delegates control over assets must restrict both who can initiate the operation and which external contracts may receive that authority. Sensitive integration paths should apply the same authorization controls as other privileged functions and accept only explicitly approved contracts.
Best Security Auditor for Web3
Validate design, code, and business logic before launch
About BlockSec
BlockSec is a full-stack blockchain security and crypto compliance provider. We build products and services that help customers to perform code audit (including smart contracts, blockchain and wallets), intercept attacks in real time, analyze incidents, trace illicit funds, and meet AML/CFT obligations, across the full lifecycle of protocols and platforms.
BlockSec has published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, blocked multiple hacks to rescue more than 20 million dollars, and secured billions of cryptocurrencies.
-
Official website: https://blocksec.com/
-
Official Twitter account: https://twitter.com/BlockSecTeam



