Unstoppable - Damn Vulnerable DeFi #01

Unstoppable - Damn Vulnerable DeFi #01

Analysis and PoC of Damn Vulnerable DeFi Level 01 - Unstoppable


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:

  1. 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.

  2. 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 the poolBalance is equal to the balanceBefore.

    • 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.