過去1週間(2026/10/05 - 2026/10/11)に、推定損失額が合計約54万2,000ドルに上るセキュリティインシデントを1件確認しました。
| 日付 | インシデント | 種類 | 推定損失額 |
|---|---|---|---|
| 2026/10/06 | MakerDaoBot | アクセス制御の欠如 | 約54万2,000ドル |
今週の注目事例:MakerDaoBot
2026年10月6日、攻撃者はEthereum上の旧式のMakerDAO清算ボットであるMakerDaoBotのアクセス制御の欠如を悪用し、約54万2,000ドル相当の200 ETHを引き出しました。脆弱な関数では、誰でも外部コントラクトを指定し、そのコントラクトにボットの残高を操作する権限を与えたうえで、そのコードを実行できました。攻撃者はこの経路を利用して、過去の担保オークション4件を決済し、解放されたETHを1回のトランザクションで送金しました。
背景
MakerDaoBotは、旧清算システムで使用されていた担保オークション用コントラクトFlipperを通じて、MakerDAOの旧式のETH-A担保オークションに参加していました。このボットはVatの権限を管理し、ETHとDAIの預け入れ・引き出しを行い、清算タスクを処理していました。
VatはMakerDAOの内部台帳です。各アドレスの担保種別ごとの利用可能な担保をgem[ilk][address]に、内部のDAI残高をdai[address]に記録します。別個のウォレットやアカウントコントラクトはありません。したがって、MakerDaoBotのVat残高とは、ボットのコントラクトアドレスに記録された台帳上の残高を指します。
hope(usr)を呼び出しても、それらの残高は移転しません。この呼び出しはcan[msg.sender][usr]を1に設定し、usrがVatのwish()権限チェックを通過して、msg.senderに記録された残高を操作できるようにします。flux()やmove()などの関数は、内部の担保や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);
}
脆弱性の分析
根本原因は、0x6839...5546にあるMakerDaoBotコントラクトの関数0x8804d1de()にアクセス制御がなかったことです。この関数はvarg0を外部コントラクトのアドレス(以下、アダプター)として扱い、そのアドレスのdrip()、vat()、続いてjoin()を呼び出しました。vat()を通じてVatのアドレスを取得した後、ボットはアダプターが自身のVat残高を操作できるか確認しました。操作できない場合はhope(varg0)を呼び出して権限を与え、同じアダプターのjoin()を実行しました。

同じコントラクトのほかの特権的なエントリーポイントは、処理を実行する前にds-authによるチェックを行っていました。以下の比較では、保護された2つのエントリーポイントがbytes4セレクターとmsg.senderを認可ヘルパーに渡し、チェックに失敗するとds-auth-unauthorizedで処理を戻す様子が示されています。関数0x8804d1de()には、同等の呼び出し元チェックがありませんでした。

そのため、関数0x8804d1de()では、誰でもVatの権限を付与されるアドレスを選べました。ボットはhope()を通じてそのアドレスに権限を与え、直後にそのjoin()関数を呼び出しました。この外部呼び出し中も権限は有効だったため、選ばれたアドレスのコードはボットの内部担保残高とDAI残高を操作できました。
攻撃の分析
以下の分析は、トランザクション0xbb6940...f3a88cに基づいています。
- ステップ1:攻撃者のEOAが、自らデプロイした攻撃用コントラクトを呼び出しました。そのコントラクトは悪意あるアダプターをデプロイし、そのアドレスを指定してMakerDaoBotの
0x8804d1de()関数を呼び出しました。ボットはVatのcanマッピングに既存の権限付与がないことを確認してhope()を呼び出し、ボットのアドレスに記録された残高をアダプターが操作できるようにしました。

- ステップ2:MakerDaoBotがアダプターの
join()関数を呼び出しました。権限が有効な間に、悪意あるコードは運用を終了したETH-A用Flipperのdeal(uint256 id)を呼び出し、オークション1457、1458、1459、1460を決済しました。その後、アダプターはVatの権限を使って、200単位のETH-A担保をMakerDaoBotから自身に移し、ETH Joinを通じて引き出して200WETHを受け取り、そのトークンを攻撃用コントラクトに送金しました。

- ステップ3:攻撃用コントラクトは200
WETHをアンラップし、当時約54万2,000ドル相当の200ETHを攻撃者のEOAに送金しました。

結論
根本原因はMakerDaoBotのアクセス制御の欠如であり、MakerDAOのVat権限システムの欠陥ではありませんでした。公開関数によって、誰でも外部コントラクトを選び、ボットの内部残高を操作する権限を与え、そのコードを実行できました。攻撃者はこの経路を利用して、旧式のオークション4件を決済し、200 ETHを引き出しました。
資産の操作権限を委譲する関数では、操作を開始できる主体と、その権限を受け取れる外部コントラクトの両方を制限する必要があります。機密性の高い連携処理には、ほかの特権的な関数と同じ認可制御を適用し、明示的に承認されたコントラクトのみを受け入れるべきです。
Web3に最適なセキュリティ監査サービス
ローンチ前に設計、コード、ビジネスロジックを検証
BlockSecについて
BlockSecは、ブロックチェーンセキュリティと暗号資産コンプライアンスを包括的に提供する企業です。プロトコルやプラットフォームのライフサイクル全体にわたり、コード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、攻撃のリアルタイム阻止、インシデント分析、不正資金の追跡、AML/CFT義務への対応を支援する製品とサービスを開発しています。
BlockSecは、権威ある学会でブロックチェーンセキュリティに関する論文を複数発表し、DeFiアプリケーションに対するゼロデイ攻撃を複数報告してきました。また、複数のハッキングを阻止して2,000万ドル超の資産を保護し、数十億ドル相当の暗号資産の安全を確保してきました。
-
公式ウェブサイト:https://blocksec.com/
-
公式Twitterアカウント:https://twitter.com/BlockSecTeam
-
🔗 BlockSec監査サービス : お問い合わせ



