Damn Vulnerable DeFi - Challenge #1 - Unstoppable
1) Challenge
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 (link).
Challenge created by @tinchoabbate.
2) Code Review
The function responsible for offering flash loans is flashLoan
, defined in the UnstoppableLender
contract (source code):
|
|
Multiple conditions need to be satisfied before providing a flash loan:
- the amount we want to borrow is more than
0
(line34
) - the balance available in the pool is greater than the amount we want to borrow (line
37
) - the value of the
poolBalance
variable is equal to the balance of the pool when requesting the flash loan (line40
) - the balance after the loan is offered is greater or equal to the initial balance (line
47
) - it checks if we paid back the loan
The amount we want to borrow is transferred to the msg.sender
(who calls flashLoan
), and the exact amount is passed to receiveTokens
of aReceiverUnstoppable
contract (it simulates a potential usage of the flash loan before paying it back). Finally, the tokens are sent back to the contract (source code).
flashLoan
function permanently false, it will no longer complete its execution, causing a Denial-of-Service.Among all the above conditions, condition 3
and 4
are the only that don’t depend on the amount we want to borrow. Since condition 4
checks if we paid back the flash loan, let’s focus on condition 3
:
|
|
depositTokens
updates both the above values: it calls transferFrom
to transfer tokens to the pool and then updates the poolBalance
variable with the new balance. Since tokens are transferred to the pool by calling trasferFrom
, even the pool balance (balanceBefore
variable in flashLoan
function) is updated:
|
|
However, the caveat here is that the values balanceBefore
and poolBalance
are retrieved from different sources:
balanceBefore
is the output ofbalanceOf
poolBalance
is a public variable updated when callingdepositTokens
depositTokens
?The answer is YES.
damnValuableToken
is an ERC-20 token, which means it implements two functions to transfer tokens:
transferFrom(address sender, address recipient, uint256 amount)
(doc here)transfer(address recipient, uint256 amount)
(doc here)
Since we have 100 DVT
tokens, we can transfer some of these tokens to the pool using the transfer
function. This way, the pool balance (balanceBefore
) will be different from the poolBalance
value because we have deposited tokens without calling depositTokens
(the poolBalance
variable is not updated).
The function flashLoan
will stop working because the condition 3
will be false.
3) Solution
The solution consists in sending some DVT
tokens to the pool using transfer
:
|
|
Output:
|
|
You can find the complete code here.