Damn Vulnerable DeFi Challenge #1 Solution — Unstoppable
Throughout numerous challenges, you will build the skills to become a bug hunter or security auditor in the space.
Challenge #1 — Unstoppable
There’s a lending pool with a million DVT tokens in balance, offering flash loans for free.
If only there was a way to attack and stop the pool from offering flash loans …You start with 100 DVT tokens in balance.
The attacker end goal
Our end goal in this challenge is to DOS (Denial of Service) the contract, preventing anyone to interact with it.
How can we break things?
Study the contracts
Let’s see what’s inside the Lending platform
We can see that
- The contract is using Solidity
>0.8. This mean that this contract will not be prone to underflow/overflow errors.
- The contract inherit from the OpenZeppelin’s ReentrancyGuard contract, so we can be sure that reentrancy will not be a problem.
- We assume that the DVT (Damn Vulnerable Token) token is a safe contract
constructoris correctly checking that the DVT token is not an empty address
Let’s see what’s inside the
- It checks that
borrowAmountis not 0
- It correctly checks that the balance of the Lending Pool has enough tokens to sustain the flashloan to the user
- It’s checking that
poolBalance(state variable that track token deposited on the pool by users) is equal to the actual token balance of the pool
- It will perform the flash loan
- It will check that the balance after the flashloan is greater than the balance before. This is needed becasue we want to be sure that the user has at least repaid their loan debt.
uint256 balanceAfter = damnValuableToken.balanceOf(address(this)); require(balanceAfter >= balanceBefore, “Flash loan hasn’t been paid back”);
The first thing that come to my mind is the SWC-132: Unexpected Ether balance that says:
Contracts can behave erroneously when they strictly assume a specific Ether balance. It is always possible to forcibly send ether to a contract (without triggering its fallback function), using selfdestruct, or by mining to the account. In the worst case scenario this could lead to DOS conditions that might render the contract unusable.
The same thing applies here. The Lending Pool is assuming that all the users will supply tokens to the pool via the
depositTokens method that will update the
poolBalance internal balance.
So, how can we break the assumption that
assert(poolBalance == balanceBefore);?
It’s pretty simple: we just make our attacker that has 100 DVT tokens to send 1 token to the Lending Pool address directly
Thereafter, you would have that
poolBalance would still be 1M tokens but the real balance of the lending pool would be 1M+1
You can see the full solution in the repository of Damn Vulnerable DeFi — Foundry edition
All Solidity code, practices and patterns in this repository are DAMN VULNERABLE and for educational purposes only.
DO NOT USE IN PRODUCTION.