This level involves a Lending Pool that contains a million DVT tokens. This pool also offers flash loans without any fee. Our job is to attack the pool and stop it from issuing flash loans. Our user (attacker) is given 100 DVT tokens to start with.
Smart Contract Analysis
There are two interesting functions here as shown below:
This function is used to receive DVT tokens and update the poolBalance with the amount of tokens received. Anyone can send DVT as long as they are sending > 0 tokens. Nothing suspicious here.
This function is used to request flash loans.
balanceBeforeis storing the current balance in the contract.
assertstatement on L19 makes sure that the
poolBalanceis equal to the
If the previous validations return true, the rest of the flash loan process is executed.
The vulnerability lies in L19's assertion. Note that the function is validating a state variable
poolBalance with the current contract's balance. This validation can easily be broken because there are multiple ways to deposit tokens in the contract. Once
poolBalance becomes less than
balanceBefore, this assert will return false and the
flashLoan() function will break.
To exploit this, we won't be calling the
depositTokens() because it'll just update the value of
poolBalance . Since the contract is using an ERC20 token, there's another function called
transfer() that does just that. It'll help us transfer the tokens from our account to the contract, and the contract won't be updating the
Here's the solution which can be used inside the test script provided
this.token.connect(attacker)is used to connect as an attacker account which is defined above in the script.
transfer(this.pool.address, 1)is used to transfer 1 DVT from the attacker's account to the pool's address.
Run the script using
yarn run unstoppable and the test case will pass.
- Strict equality checks with tokens or Ether on sensitive functions are a bad idea because the contract can be force-fed tokens/Ether externally.