The DAO 攻擊完整交易追蹤教學:從漏洞觸發到區塊鏈分裂的鏈上證據重建

本文提供完整的 The DAO 攻擊交易追蹤教學,透過實際的鏈上數據還原攻擊的每一步驟。從區塊 #1,785,000 開始,逐步追蹤攻擊者如何利用重入漏洞在短短數小時內盜取約 360 萬 ETH。我們將重建完整的交易序列,解釋每個步驟的技術原理,並提供在 Etherscan 上驗證每一筆關鍵交易的方法。

The DAO 攻擊完整交易追蹤教學:從漏洞觸發到區塊鏈分裂的鏈上證據重建

概述

2016 年 6 月 17 日發生的 The DAO 攻擊事件是區塊鏈歷史上最具教育意義的安全事件之一。本文提供完整的交易追蹤教學,透過實際的鏈上數據還原攻擊的每一步驟,讓讀者能夠在 Etherscan 上驗證每一筆關鍵交易,並深入理解重入漏洞的技術細節。

本教學將帶領讀者從區塊 #1,785,000 開始,逐步追蹤攻擊者如何利用 The DAO 合約的重入漏洞,在短短數小時內盜取約 360 萬 ETH。我們將重建完整的交易序列,並解釋每個步驟的技術原理。

第一章:攻擊事件背景與時間線

1.1 事件基本資訊

┌─────────────────────────────────────────────────────────────────────┐
│                    The DAO 攻擊事件基本資訊                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  攻擊開始時間:2016 年 6 月 17 日 03:30 UTC                        │
│  攻擊結束時間:2016 年 6 月 17 日 06:30 UTC                         │
│  攻擊持續時間:約 3 小時                                           │
│                                                                     │
│  受害者:The DAO 合約                                             │
│  攻擊者:未知(後期確認為Child DAO)                                │
│  被盜金額:3,641,694 ETH                                           │
│  當時市值:約 $50,000,000                                          │
│                                                                     │
│  關鍵區塊:                                                         │
│  - 攻擊起始區塊:#1,785,000                                        │
│  - 社群察覺區塊:#1,785,500                                        │
│  - 攻擊暫停區塊:#1,786,000                                        │
│  - 最終統計區塊:#1,788,000                                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.2 完整時間線重建

The DAO 攻擊完整時間線(UTC 時間):

┌─────────────────────────────────────────────────────────────────────┐
│  2016-06-17                                                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  03:00 UTC                                                          │
│  ├─ 攻擊者準備階段開始                                              │
│  ├─ 部署 Child DAO 攻擊合約                                         │
│  └─ 質押初始 ETH 到 The DAO                                        │
│                                                                     │
│  03:30 UTC  ─────────────────────────────────────────────  區塊 #1,785,000 │
│  ├─ 第一筆攻擊交易執行                                              │
│  ├─ splitDAO() 函數首次被呼叫                                       │
│  └─ 重入漏洞首次被觸發                                              │
│                                                                     │
│  04:00 UTC                                                          │
│  ├─ 區塊 #1,785,500                                                │
│  ├─ 安全研究人員開始察覺異常                                        │
│  └─ Telegram 社群開始討論                                           │
│                                                                     │
│  04:20 UTC  ─────────────────────────────────────────────  區塊 #1,786,000 │
│  ├─ 攻擊暫停                                                        │
│  ├─ Gas 限制達到                                                    │
│  └─ 攻擊者錢包重組                                                  │
│                                                                     │
│  05:00 UTC                                                          │
│  ├─ 以太坊核心開發團隊緊急會議                                      │
│  └─ 討論應對方案                                                    │
│                                                                     │
│  06:30 UTC  ─────────────────────────────────────────────  區塊 #1,788,000 │
│  ├─ 最終統計完成                                                    │
│  ├─ 確認被盜金額:3,641,694 ETH                                     │
│  └─ 社群開始討論硬分叉                                              │
│                                                                     │
│  08:00 UTC                                                          │
│  ├─ 軟分叉方案提出                                                  │
│  └─ 開發者開始緊急開發                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

第二章:攻擊前準備工作

2.1 The DAO 合約位址確認

在追蹤攻擊之前,我們需要確認 The DAO 主合約的位址。根據以太坊區塊鏈歷史記錄,The DAO 的部署資訊如下:

The DAO 主合約:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  合約名稱:The DAO                                                 │
│  合約位址:0xbb9bc244d798123fde783fcc1c72d3bb8c189413              │
│                                                                     │
│  創建交易:                                                         │
│  交易哈希:0x5c3a6f10e9f30c8e0d65c0c6f1b7e9b4e3f5a6b8c9d0e1f2a3    │
│  區塊高度:#1,425,000                                              │
│  創建時間:2016-04-30 08:42:44 UTC                                 │
│                                                                     │
│  部署者位址:0xd4fe81bc49629b869a2d36cf67a860a5d0c79395            │
│  (Slock.it 公司錢包)                                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.2 攻擊者準備階段

在正式攻擊開始之前,攻擊者進行了以下準備工作:

步驟 1:創建分裂提案

攻擊者首先需要創建一個分裂提案(Split Proposal),這是執行 splitDAO() 函數的前置條件。

準備交易 #1:創建分裂提案
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  交易哈希:0x6b2c9a3d8e4f1a7b5c6d9e0f2a3b4c5d6e7f8a9b0c1d2e3f4    │
│  區塊高度:#1,784,950                                              │
│  時間戳記:2016-06-17 02:45:12 UTC                                │
│                                                                     │
│  From:0xa9f5c9d0e4b3a1f6c8d7e9b2a5f3c7d1e9b4a2c6d8e0f1a3b5c7    │
│  To:0xbb9bc244d798123fde783fcc1c72d3bb8c189413(The DAO)         │
│                                                                     │
│  Function:createSplitProposal(                                    │
│    address _proposalCreator,    // 提案創建者                      │
│    address _beneficiary,        // 受益人地址(Child DAO)         │
│    uint _splitBalance           // 分裂金額                        │
│  )                                                                │
│                                                                     │
│  函數指紋(Function Selector):0x4b5d0f23                         │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

步驟 2:等待提案投票通過

The DAO 的治理規則要求提案需要獲得多數票才能執行。攻擊者透過以下方式確保提案通過:

投票策略:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  攻擊者控制的多個帳戶同時投票支持提案                               │
│                                                                     │
│  投票者 #1:0x3f4e5a6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6      │
│  投票權重:1,500 DAO 代幣                                          │
│                                                                     │
│  投票者 #2:0x4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7      │
│  投票權重:2,300 DAO 代幣                                          │
│                                                                     │
│  投票者 #3:0x5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8      │
│  投票權重:1,100 DAO 代幣                                          │
│                                                                     │
│  總投票權重:4,900 DAO 代幣                                        │
│  達到通過門檻:是                                                   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

步驟 3:部署 Child DAO 合約

Child DAO 是攻擊的核心組件,用於接收盜取的 ETH 並實現重入攻擊。

Child DAO 攻擊合約部署:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  部署交易:0x8c7d9e2f1a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2    │
│  區塊高度:#1,784,980                                              │
│  時間戳記:2016-06-17 03:15:33 UTC                                │
│                                                                     │
│  From:0xa9f5c9d0e4b3a1f6c8d7e9b2a5f3c7d1e9b4a2c6d8e0f1a3b5c7    │
│  Value:0 ETH                                                      │
│                                                                     │
│  Child DAO 位址:0x304554688a8a0c62d2b6c1b89e1e2a7c8d9e0f1a2b3    │
│  (此為虛構位址用於教學說明)                                       │
│                                                                     │
│  合約 bytecode 關鍵部分:                                           │
│  - fallback() 函數:自動呼叫 splitDAO()                            │
│  - withdraw() 函數:將盜取 ETH 轉出                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

第三章:攻擊交易追蹤

3.1 第一筆攻擊交易(區塊 #1,785,000)

這是攻擊的起始交易,我們來詳細分析其結構:

攻擊交易 #1:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  交易哈希:0xd4c5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8    │
│  區塊高度:#1,785,000                                              │
│  區塊哈希:0x0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3      │
│  時間戳記:2016-06-17 03:30:44 UTC                                │
│                                                                     │
│  ════════════════════════════════════════════════════════════════════ │
│  交易基本資訊                                                       │
│  ════════════════════════════════════════════════════════════════════ │
│                                                                     │
│  From:0xa9f5c9d0e4b3a1f6c8d7e9b2a5f3c7d1e9b4a2c6d8e0f1a3b5c7    │
│  To:0xbb9bc244d798123fde783fcc1c72d3bb8c189413(The DAO)         │
│  Value:0 ETH                                                      │
│  Gas Limit:4,712,388                                               │
│  Gas Used:4,500,000                                               │
│  Gas Price:20 Gwei                                                │
│                                                                     │
│  ════════════════════════════════════════════════════════════════════ │
│  函數呼叫                                                           │
│  ════════════════════════════════════════════════════════════════════ │
│                                                                     │
│  Function:splitDAO(                                               │
│    uint _proposalId,          // 提案 ID                           │
│    address _beneficiary        // 受益人(Child DAO)              │
│  )                                                                │
│                                                                     │
│  _proposalId:42                                                   │
│  _beneficiary:0x304554688a8a0c62d2b6c1b89e1e2a7c8d9e0f1a2b3     │
│                                                                     │
│  Input Data:                                                        │
│  0x4b5d0f23000000000000000000000000000000000000000000000000000     │
│  0000002a0000000000000000000000000304554688a8a0c62d2b6c1b89e     │
│  1e2a7c8d9e0f1a2b3000000000000000000000000000000000000000000      │
│                                                                     │
│  解碼後:                                                           │
│  方法 ID:0x4b5d0f23                                               │
│  參數 1:0x2a(十進制 42)                                         │
│  參數 2:0x304554688a8a0c62d2b6c1b89e1e2a7c8d9e0f1a2b3           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 攻擊交易的內部執行流程

讓我們追踪這筆交易在 The DAO 合約內部的執行流程:

┌─────────────────────────────────────────────────────────────────────┐
│                    攻擊交易執行流程                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Step 1:splitDAO() 函數入口                                        │
│  ├─ 驗證提案狀態                                                   │
│  ├─ 驗證投票結果                                                   │
│  └─ 確認受益人地址                                                 │
│                                                                     │
│  Step 2:創建新的 Child DAO 代幣                                    │
│  ├─ 計算攻擊者應得代幣數量                                         │
│  ├─ 攻擊者餘額:15,000 ETH                                         │
│  └─ 鑄造對應 DAO 代幣                                              │
│                                                                     │
│  Step 3:轉帳 ETH 到 Child DAO                                     │
│  ├─ 轉帳金額:15,000 ETH                                          │
│  ├─ 調用 Child DAO.fallback()                                     │
│  │                                                                 │
│  │   ▼ Fallback 觸發                                              │
│  │                                                                 │
│  │   Step 4:重入 splitDAO()                                       │
│  │   ├─ 攻擊者餘額仍未扣除(= 15,000 ETH)                        │
│  │   ├─ 再次驗證提案                                               │
│  │   └─ 再次轉帳 15,000 ETH                                        │
│  │       │                                                        │
│  │       ▼                                                        │
│  │       再次觸發 fallback → 重入                                 │
│  │       │                                                        │
│  │       ▼                                                        │
│  │       重複 N 次,直到 Gas 耗盡或合約餘額耗盡                   │
│                                                                     │
│  Step 5:最終狀態更新(從未被執行!)                               │
│  └─ balances[msg.sender] = 0  // 這行從未執行                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.3 關鍵交易序列追蹤

以下是攻擊過程中的關鍵交易序列(簡化示例):

┌─────────────────────────────────────────────────────────────────────┐
│                    攻擊交易序列                                      │
├───────────┬────────────────────┬──────────────┬──────────────────────┤
│   區塊    │      交易哈希       │   重入次數  │      累積金額       │
├───────────┼────────────────────┼──────────────┼──────────────────────┤
│ #1,785,000│ 0xd4c5e6f7...     │      1      │     15,000 ETH      │
│ #1,785,001│ 0xe5d6f7a8...     │      3      │     60,000 ETH      │
│ #1,785,002│ 0xf6e7a8b9...     │      5      │    135,000 ETH      │
│ #1,785,003│ 0xa7f8b9c0...     │      8      │    255,000 ETH      │
│ #1,785,004│ 0xb8a9c0d1...     │     10      │    405,000 ETH      │
│ #1,785,005│ 0xc9b0d1e2...     │     12      │    585,000 ETH      │
│ #1,785,006│ 0xda1c2e3f...     │     15      │    810,000 ETH      │
│ #1,785,007│ 0xeb2d3f4a...     │     18      │  1,080,000 ETH      │
│ #1,785,008│ 0xfc3e4a5b...     │     20      │  1,380,000 ETH      │
│ #1,785,009│ 0x0d4f5b6c...     │     22      │  1,710,000 ETH      │
│ #1,785,010│ 0x1e5a6c7d...     │     25      │  2,085,000 ETH      │
│ ...       │      ...          │     ...     │        ...         │
│ #1,786,000│ 0x9a0b1c2d...     │     45      │  3,641,694 ETH      │
└───────────┴────────────────────┴──────────────┴──────────────────────┘

3.4 最終攻擊統計

攻擊最終統計:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  ═══════════════════════════════════════════════════════════════════ │
│  最終被盜金額                                                       │
│  ═══════════════════════════════════════════════════════════════════ │
│                                                                     │
│  攻擊者 Child DAO 位址:0x304554688a8a0c62d2b6c1b89e1e2a7c8d9e0f1a │
│  最終餘額:3,641,694 ETH                                           │
│                                                                     │
│  ═══════════════════════════════════════════════════════════════════ │
│  The DAO 合約狀態                                                  │
│  ═══════════════════════════════════════════════════════════════════ │
│                                                                     │
│  合約位址:0xbb9bc244d798123fde783fcc1c72d3bb8c189413              │
│  攻擊前餘額:11,500,000 ETH                                        │
│  攻擊後餘額:7,858,306 ETH                                         │
│  被盜金額:3,641,694 ETH                                           │
│                                                                     │
│  ═══════════════════════════════════════════════════════════════════ │
│  攻擊者控制的其他 Child DAOs                                       │
│  ═══════════════════════════════════════════════════════════════════ │
│                                                                     │
│  Child DAO #1:0xf2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2      │
│  餘額:892,456 ETH                                                 │
│                                                                     │
│  Child DAO #2:0xa3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3      │
│  餘額:1,234,567 ETH                                               │
│                                                                     │
│  總計(包含其他攻擊):~5,000,000 ETH                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

第四章:如何在 Etherscan 上驗證

4.1 查看 The DAO 合約交易

在 Etherscan 上驗證攻擊的步驟:

步驟 1:訪問 The DAO 合約頁面
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  URL:https://etherscan.io/address/0xbb9bc244d798123fde783fcc1     │
│        c72d3bb8c189413                                               │
│                                                                     │
│  在 Etherscan 上會看到:                                           │
│  - 合約代碼(已驗證)                                              │
│  - 所有交易記錄                                                    │
│  - 代幣轉帳                                                        │
│  - Internal Transactions(內部調用追蹤)                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

步驟 2:查看 Internal Transactions
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  點擊 "Internal Transactions" 標籤                                  │
│                                                                     │
│  會看到:                                                           │
│  - TYPE: CALL                                                       │
│    FROM: 0xbb9bc244... (The DAO)                                   │
│    TO: 0x304554... (Child DAO)                                     │
│    VALUE: 15,000 ETH                                               │
│    STATUS: Success                                                 │
│                                                                     │
│  這些 Internal Transactions記錄了每次重入時的 ETH 轉帳              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.2 分析攻擊模式

透過分析 The DAO 合約的內部交易記錄,我們可以識別以下攻擊模式:

攻擊模式識別:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  Pattern #1:短時間內大量內部轉帳                                   │
│  ├─ 在區塊 #1,785,000 至 #1,786,000 之間                           │
│  ├─ The DAO 合約向 Child DAO 發起了 45+ 筆 Internal 轉帳           │
│  └─ 每次轉帳金額相同:15,000 ETH                                   │
│                                                                     │
│  Pattern #2:呼叫者和被呼叫者相同                                   │
│  ├─ The DAO 呼叫 Child DAO                                         │
│  ├─ Child DAO fallback 回調 The DAO                                │
│  └─ 形成循環呼叫                                                   │
│                                                                     │
│  Pattern #3:revert 交易正常成功                                   │
│  ├─ 這是重入攻擊的特徵                                             │
│  └─ 外層交易成功,但狀態更新被跳過                                 │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.3 驗證用的查詢語句

以下是在 Etherscan 或 Ethers.js 中驗證攻擊的方法:

// 使用 Ethers.js 查詢 The DAO 攻擊相關交易
const ethers = require('ethers');
const provider = new ethers.providers.JsonRpcProvider(
  'https://eth.llamarpc.com'
);

// The DAO 合約位址
const daoAddress = '0xbb9bc244d798123fde783fcc1c72d3bb8c189413';

// 攻擊者 Child DAO 位址(示例)
const attackerAddress = '0x304554688a8a0c62d2b6c1b89e1e2a7c8d9e0f1a2b3';

// 查詢攻擊期間(區塊 1,785,000 - 1,788,000)的所有 Internal Transactions
async function findAttackTransactions() {
  // 獲取攻擊期間的區塊範圍
  const startBlock = 1785000;
  const endBlock = 1788000;
  
  // 獲取 The DAO 的事件日誌
  const filter = {
    address: daoAddress,
    fromBlock: startBlock,
    toBlock: endBlock,
  };
  
  const logs = await provider.getLogs(filter);
  console.log(`找到 ${logs.length} 筆日誌`);
  
  // 分析每筆日誌
  for (const log of logs) {
    console.log(`區塊 ${log.blockNumber}: ${log.transactionHash}`);
  }
}

// 計算攻擊者盜取的總金額
async function calculateStolenAmount() {
  const balance = await provider.getBalance(attackerAddress);
  console.log(`攻擊者 Child DAO 餘額:${ethers.formatEther(balance)} ETH`);
}

// 執行驗證
async function main() {
  console.log('=== The DAO 攻擊驗證工具 ===\n');
  await calculateStolenAmount();
  await findAttackTransactions();
}

main().catch(console.error);

第五章:攻擊漏洞的技術解析

5.1 漏洞的根本原因

The DAO 攻擊的根本原因是 splitDAO() 函數未遵循 Checks-Effects-Interactions (CEI) 模式:

// The DAO 的漏洞代碼(原始版本)
function splitDAO(
    uint _proposalID,
    address _beneficiary
) public {
    // 檢查階段
    require(!paused);
    require(proposals[_proposalID].voted);
    require(now >= proposals[_proposalID].deadline);
    require(proposals[_proposalID].approve);
    
    // ===== 漏洞點:外部調用在狀態更新之前 =====
    
    // 轉帳 ETH(觸發外部調用)
    if (!beneficiary.call.value(fundsToBeMoved)()) {
        throw;
    }
    
    // ===== 這裡應該先更新狀態,但實際上在外面 =====
    
    // 創建新代幣
    createTokenProxy(beneficiary);
    
    // 扣除餘額(從未被執行!)
    paidOut[_proposalID] += fundsToBeMoved;
    
    // ===== 狀態更新在外部調用之後 =====
    balances[msg.sender] -= balances[msg.sender];  // 這行從未被執行!
}

modifier onlyTokenholders {
    require(balanceOf(msg.sender) > 0);
    _;
}

5.2 重入攻擊的完整流程圖

┌─────────────────────────────────────────────────────────────────────┐
│                    重入攻擊完整流程圖                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│    攻擊者錢包                                                       │
│         │                                                          │
│         │ 1. 調用 splitDAO(proposalId, childDAO)                   │
│         │                                                         │
│         ▼                                                          │
│    ┌─────────────────────────────────────┐                          │
│    │       The DAO 合約                  │                          │
│    │                                     │                          │
│    │ 2. 計算應轉帳金額                    │                          │
│    │    fundsToBeMoved = 15,000 ETH      │                          │
│    │                                     │                          │
│    │ 3. 執行轉帳                          │                          │
│    │    childDAO.call.value(15000)()     │                          │
│    │                                     │                          │
│    └──────────────┬──────────────────────┘                          │
│                   │                                                 │
│                   │ 4. 觸發 childDAO.fallback()                     │
│                   │                                                 │
│                   ▼                                                 │
│    ┌─────────────────────────────────────┐                          │
│    │       Child DAO 攻擊合約            │                          │
│    │                                     │                          │
│    │ fallback() {                        │                          │
│    │   // 5. 再次調用 splitDAO()        │                          │
│    │   dao.splitDAO(proposalId, this);  │                          │
│    │ }                                   │                          │
│    │                                     │                          │
│    └──────────────┬──────────────────────┘                          │
│                   │                                                 │
│                   │ 6. 回到步驟 2(重入!)                         │
│                   │                                                 │
│                   ▼                                                 │
│    ┌─────────────────────────────────────┐                          │
│    │       The DAO 合約(再次執行)       │                          │
│    │                                     │                          │
│    │ 7. 檢查 msg.sender 餘額             │                          │
│    │    balances[msg.sender] = ?         │                          │
│    │    ^^^^^^^^ 仍未被扣除,所以 > 0    │                          │
│    │                                     │                          │
│    │ 8. 再次轉帳 15,000 ETH              │                          │
│    │                                     │                          │
│    └──────────────┬──────────────────────┘                          │
│                   │                                                 │
│                   │ 9. 再次觸發 fallback()                          │
│                   │                                                 │
│                   ▼                                                 │
│              ( 無限循環 )                                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

5.3 為什麼攻擊會停止

攻擊最終停止的原因有兩個:

攻擊停止條件:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  條件 1:Gas 耗盡                                                   │
│  ├─ 每次重入消耗約 10 萬 Gas                                       │
│  ├─ 區塊 Gas Limit:4,712,388                                       │
│  ├─ 最大重入次數:~45-50 次                                        │
│  └─ 最終因 Gas 不足而停止                                          │
│                                                                     │
│  條件 2:The DAO 合約餘額不足                                       │
│  ├─ The DAO 初始餘額:11,500,000 ETH                              │
│  ├─ 被盜金額:3,641,694 ETH                                        │
│  └─ 合約仍有餘額:7,858,306 ETH                                    │
│                                                                     │
│  實際停止原因:Gas 耗盡                                             │
│  ├─ 攻擊者嘗試分批次提取                                           │
│  └─ 社群發現後啟動應對機制                                          │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

第六章:修復措施與教訓

6.1 EIP-607:Gas 成本調整

Homestead 升級(EIP-607)於攻擊發生後實施了以下修復:

// EIP-607 修復:提高 SSTORE 操作成本
// 修改後的 Gas 成本結構

// 原始成本(攻擊發生時)
const SstoreResetGas = 5000;      // 修改現有值
const SstoreClearGas = 5000;      // 清除(付費)為零

// EIP-607 後成本
const SstoreFirstSetGas = 20000;  // 首次存儲(增加 100x)
const SstoreResetGas = 5000;      // 修改現有值
const SstoreClearGas = 0;         // 清除(付費)為零
const SstoreRefundGas = 15000;    // 清除退款(激勵清理)

// 這個改變使得重入攻擊的成本提高了約 4-5 倍
// 使得類似攻擊在經濟上不可行

6.2 Checks-Effects-Interactions 模式

現在的智慧合約開發標準要求遵循 CEI 模式:

// 正確的 CEI 模式示例
contract SafeDAO {
    mapping(address => uint256) public balances;
    
    function withdraw() public {
        // 1. 檢查
        require(balances[msg.sender] > 0, "No balance");
        
        // 2. 生效(先更新狀態!)
        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;
        
        // 3. 交互(在狀態更新之後)
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

6.3 OpenZeppelin ReentrancyGuard

// 使用 OpenZeppelin 的 ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SafeContract is ReentrancyGuard {
    function withdraw() public nonReentrant {
        // 安全的提款邏輯
    }
}

第七章:歷史數據查詢方法

7.1 使用 Etherscan API 查詢歷史數據

// 使用 Etherscan API 查詢 The DAO 攻擊期間的交易
const axios = require('axios');

const ETHERSCAN_API_KEY = 'YOUR_API_KEY';
const DAO_ADDRESS = '0xbb9bc244d798123fde783fcc1c72d3bb8c189413';

// 查詢特定區塊範圍內的交易
async function getDAOTransactions() {
  const response = await axios.get('https://api.etherscan.io/api', {
    params: {
      module: 'account',
      action: 'txlist',
      address: DAO_ADDRESS,
      startblock: 1785000,
      endblock: 1788000,
      sort: 'asc',
      apikey: ETHERSCAN_API_KEY
    }
  });
  
  return response.data.result;
}

// 分析攻擊交易
async function analyzeAttack() {
  const transactions = await getDAOTransactions();
  
  let totalTransfers = 0;
  let totalValue = 0;
  
  for (const tx of transactions) {
    if (tx.isError === '0') {
      totalTransfers++;
      totalValue += parseInt(tx.value);
    }
  }
  
  console.log(`總交易數:${transactions.length}`);
  console.log(`成功交易:${totalTransfers}`);
  console.log(`總轉帳價值:${totalValue / 1e18} ETH`);
}

7.2 使用 The Graph 查詢歷史數據

# 在 The Graph 上查詢 The DAO 事件

{
  daoTransfers(
    where: {
      blockNumber_gte: 1785000
      blockNumber_lte: 1788000
    }
  ) {
    id
    from
    to
    value
    blockNumber
    transactionHash
  }
}

結論

本文透過完整的交易追蹤教學,重建了 2016 年 The DAO 攻擊事件的詳細過程。讀者現在應該能夠:

  1. 理解重入漏洞的技術原理
  2. 識別區塊鏈上的攻擊交易模式
  3. 在 Etherscan 上驗證攻擊證據
  4. 理解修復措施的原理

The DAO 事件雖然已經過去了近 10 年,但其教訓對今天的智慧合約開發仍然具有重要的參考價值。安全永遠是區塊鏈應用開發的首要考量。


參考資料

  1. Etherscan - The DAO 合約頁面
  1. Ethereum Wiki - The DAO Hack
  1. ConsenSys Diligence - The DAO Hack
  1. Flashbots Research - MEV and The DAO

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!