Unstoppable - Damn Vulnerable DeFi #01
Analysis and PoC of Damn Vulnerable DeFi Level 01 - Unstoppable
Objectives
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
UnstoppableLender.sol
There are two interesting functions here as shown below:
depositTokens()
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.
flashLoan()
This function is used to request flash loans.
The parameter
balanceBefore
is storing the current balance in the contract.The
assert
statement on L19 makes sure that thepoolBalance
is equal to thebalanceBefore
.If the previous validations return true, the rest of the flash loan process is executed.
The Exploitation
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 poolBalance
value.
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.
Key Takeaways
- Strict equality checks with tokens or Ether on sensitive functions are a bad idea because the contract can be force-fed tokens/Ether externally.