Damn Vulnerable DeFi Challenges Walkthrough Part I (1–4)
Challenges : https://www.damnvulnerabledefi.xyz/
Damn Vulnerable DeFi : https://github.com/OpenZeppelin/damn-vulnerable-defi.git
Set up the challenges
git clone https://github.com/OpenZeppelin/damn-vulnerable-defi.git
Challenge 1 Unstoppable
The first challenge is to ask attackers to stop the lending pool from offering flash loans
Start the challenge locally
Running the challenge and we encountered the expected fail
We need to figure out the failing reason
Look at the unstoppable.challenge.js file .
We see the comment “ your exploit goes here”. The attack code should go this part .
Let’s analyze the logic behind the codes
The function executeFlashLoan() performs the ownercheck, and call flashLoan() in the UnstoppableLender contract
In this contract , the flashLoan() function lends funds to the user with several conditions check.
assert poolBalance to be equal to balanceBefore. This design is very weird. If we could make the two functions not equal, then we could successfully make the DeFi app fail.
Look at the balanceBefore function. it’s a local variable in the flashLoan() function, equals to current token balance
While Pool Balance is a global variable initialized at contract deployment time. It will only increase when depositTokens() function is called. It is under such condition that the only way to send tokens to the pool contract is using the depositTokens() function. However, if the token is transferred to the pool through ERC20 standard transfer , balanceBefore will increase because the balance increases. But poolBalance will not increase (because we do not use the depositTokens() function)
Therefore the flashLoan() calls will fail due to the inequality
Now we figure out the logic behind the codes and we can edit the stoppable.challenge.js by adding the ERC-20 transfer call to the pool contract
Run the challenge again
Passed ! We break the DeFi app !
Funds locking bugs are very common in DeFi . In reality, Percent Finance permanently locked up $1M worth of crypto due to this type of error in their smart contract implementation.
Challenge 2 Naive receiver
This challenge wants us to clear out all ETH funds from the user’s contract
Look at the challenge file
We see there is a LenderPool and a FlashLoanReceiver pool .
Let’s go through the contracts
Every time the flashLoan() function is called, 1 ETH is reduced from the contract . So we want to keep calling the flashLoan() , then the balance will keep reducing till empty
However this method will take too much time to empty the contract. We want to get the job done in a single transaction. We need to write an attacker contract and put it in the contracts/attacker-contracts directory
Write our exploit , call the attacker contract
Run the challenge , and PASSED !
Challenge 3 Truster
The challenge 3 asks us to steal all DVT tokens from the pool ! This is very exciting !
Let’s go through the contracts
there’s only one contract TrusterLenderPool.sol
flashLoan() function can lend any requested amount to the borrower address and return the amount at the end of the transaction
We can use an approve() function to transfer any amounts on the contract. We don’t use transfer() function because it will not meet the requirement balanceAfter ≥ balanceBefore
Here comes up the solution, we call the flashLoan() and borrow 0 amount, whilew waiting for approve() to be executed, and take out all from token
In this method, we are abusing the ERC-20 approval mechanism to create a transfer allowance for the damnValuableToken that will be used in a later transaction. We write a transaction payload and pass it to the vulnerable flashLoan()
The above payload will cause the TrusterLenderPool contract to create an allowance for all of the tokens held in the pool. We transfer that entire allowance to the attacker’s address.
Run the challenge and Passed !
This type of vulnerability happened to many crypto company (see the below news)
The crypto company Bancor has detected a critical vulnerability in its 1-day old smart contracts, putting $590,000 of its users’ assets at risk.
Bancor team reported that funds are now safe, however, the vulnerability is still very much alive and can be used to hack users’ future deposits.
Challenge 4 Side Entrance
Similarly as last challenge, it asks us to take out all ETH from the lending pool
Take a look at the contracts
Different with the call function from last challenge, we can only use the execute() function in receiver
From the code, we could borrow an amount of ETH from the loan pool and deposit with deposit() function . While address(this).balance ≥ balanceBefore, balance[msg.sender] will increase. Then we can withdraw() the balance from the pool
So we should craft an attacker contract in the attacker-contract directory
craft our payload
Run the challenge