Wargame Provider: @tinchoabbate
Challenge #2 — Naive receiver
There’s a lending pool offering quite expensive flash loans of Ether, which has 1000 ETH in balance. You also see that a user has deployed a contract with 10 ETH in balance, capable of interacting with the lending pool and receiveing flash loans of ETH. Drain all ETH funds from the user’s contract. Doing it in a single transaction is a big plus ;)
- We are using the Address contract of the openzeppelin library as a dependency.
uint256 private constant FIXED_FEE
- It is assigned a value of
1 etherand has a constant variable characteristic, so it cannot be modified as a fixed value at
compile-time, and it is
privateand cannot be accessed from outside.
You can define
State Variables. Both variables cannot modify their values after they are constructed.
constant variablescannot be tampered with because they are fixed at
compile-time.immutable variables are still assignable at construction-time.
function fixedFee() external pure returns (uint256)
You can see that the State Variable value is returned as it is without modifying the state value. Used for checking the FIXED_FEE value.
function flashLoan(address borrower, uint256 borrowAmount) external nonReentrant
Execute the flash loan logic, which is a core function in the current contract.
- The local variable
balanceBeforeis assigned the current contract balance value.
balanceBefore value must be greater than or equal to the parameter
borrowAmount, and the balance of the current contract must be higher than the borrowing price to be able to borrow.
✅ The parameter
borrower is assigned an address type. Under the current condition, call the
isContract function of the openzeppelin internal address contract to measure the size of the target address to measure whether the contract is activated.
The isContract function has the following characteristics.
- It is not safe to assume that the address for which this function returns false is an External Owner Account, not a contract.
- relies on extcodesize returning 0 for the contract being created because the code is only saved when the constructor execution ends
- extcodesize returns 0 if called in the contract constructor
- If you are using this in a security-sensitive setup, you should consider if it’s a problem.
- Call the
functionCallWithValuemethod with the parameter borrower. In the low-level stage, the
receiveEther(uint256)method of the target contract FlashLoanReceiver is called and used as the
✅ After the flash loan logic is completed, it is checked whether the current contract’s balance value is greater than or equal to the
balanceBefore+FIXED_FEE value to determine whether the progress has been made normally.
- I am using the Address contract of the openzeppelin library as a dependency.
- You can use various functions related to Address Type.
address payable private pool
- This is the contract address used for pool management in the flash loan process.
constructor(address payable poolAddress)
- When creating a distribution, it is possible to assign a contract address to the State Variable pool according to the value of the parameter.
function receiveEther(uint256 fee) pulibc payable
✅ The address value of the
msg.sender address and the pool variable must be the same, and you can confirm that the Sender is the contract address assigned to the pool variable.
- After adding the parameter
fee value and msg.value value, it is assigned to the
- The value assigned to the fee is
1 ether, which is the fixed fee value assigned in the
NaiveReceiverLenderPoolcontract. Even if the
msg.valuevalue changes, you can see that the fee value is fixed.
✅ The current contract’s balance value must be greater than or equal to the
amountToBeRepaid value to pass the condition, and the borrow amount value must not be greater than the balance in the holding pool.
- Call the meaningless
_executeActionDuringFlashLoan()function. (gas waste)
- Use the openzeppelin library Address contract method
sendValueto send the loan return to the
Vulnerable targets cause problems in the logic of flash Loan. Because the flashLoan function can be called externally, there is no way to reject a large number of incoming loans by calling the Receiver with this address. By providing the address you choose to pass on to the borrower to accept the flashLoan, you can do a fee and charge a flash Loan, which can ruin that Receiver’s pool.
In the current state, the externally callable Flash Loan is called. At this time, since the
FIXED_FEE value is fixedly 1 Ether, there is no need to specify the loan amount, and 10 calls are made according to the target amount of 10 Ether to exhaust the balance value of the target contract.
I will continue to post auditing and research on 12 challenge defi Smart Contracts.
Thank you for the @tinchoabbate that made a good wargame. Damn Vunlerable Defi