✅Side Entrance
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.
TL;DR
We can borrow flashloan and immediately deposit the money you just borrowed into the same contract. By doing so, the invariant in flashLoan() is bypassed and we can withdraw balance later.
Code Audit
The contract implements 3 functions:
deposit()withdraw()flashLoan()
deposit() function:
function deposit() external payable {
unchecked {
balances[msg.sender] += msg.value;
}
emit Deposit(msg.sender, msg.value);
}Looks ok.
withdraw() function:
Follows checks-effects-interactions pattern, looks ok.
flashLoan() function:
At IFlashLoanEtherReceiver(msg.sender).execute{value: amount}() we can call flashLoan() to borrow all the fund in this pool. Next we have to find out a way to bypass the check:
This check can be bypassed by calling deposit() once we receive the flash loan. The attack steps:
The attack contract's
pwn()function callsflashLoan()to borrow all the fund in the pool. In this stepIFlashLoanEtherReceiver(msg.sender).execute{value: amount}()will be called.In attack contract's
execute()function calldeposit()to deposit the flash loan we just borrowed. At this stage theif (address(this).balance < balanceBefore)check will be bypassed because the borrowed money was deposited into the same contract, thereforeaddress(this).balance == balanceBefore.When
flashLoan()finishes executing, callwithdraw()to take all the money out.
Building PoC

Last updated