Damn Vulnerable DeFi Challenge #8 Solution — Puppet

Challenge #8 — Puppet

The attacker end goal

We start with 25 ETH and 1000 DVTs in balance and we need to drain all the Pool 100000 DVTs.

Study the contracts

PuppetPool.sol

  • function borrow(uint256 borrowAmount) public payable nonReentrant allow the user to borrow borrowAmount amount of token only if the user pay at least an amount of ETH equal to the double of the token price. If the user has paid more than requested, the difference is sent back to the user.
  • function calculateDepositRequired(uint256 amount) public view returns (uint256) that will calculate the amount of ETH you need to deposit given the amount of tokens you would like to borrow. Math seems to be fine, the order of operations to not incur in meth rounding error is respected.
  • function _computeOraclePrice() private view returns (uint256) that will calculate the price of the token in the Uniswap V1 exchange DVT-ETH. This price is used by calculateDepositRequired to calculate the amount of ether needed to be deposited to borrow the tokens. Also here, the math seems to be fine, the order of operations to not incur in meth rounding error is respected.
uint256 tokenPrice = uniswapPair.balance * (10 ** 18) / token.balanceOf(uniswapPair);
uint256 depositRequired = amount * tokenPrice * 2 / 10 ** 18

Solution code

Let’s look at the attacker code and explain step by step. This is just a part of the test’s code. If you want the full solution, please go to the end of the article and see the GitHub project link.

  1. Approve the Uniswap exchange to handle all the tokens that we own (up to the infinite)
  2. Sell all the tokens that we own for some ETH. We are not interested to know how much we are going to gain, but currently we know that 1 ETH = 1 DVT. tokenToEthSwapInput(token.balanceOf(attacker), 1, deadline) will perform a swap saying: sell all the token and at least I want 1 ETH back (the minimum amount of tokenOut we expect). Make the transaction fail if it does not succeed before the specified deadline. After the swap, the price of the DVT token calculated by the Oracle inside the Puppet pool will drop. This will mean that for just a little ETH (the collateral) we will be able to borrow all the DVTs that are inside the pool.
  3. We calculate how much ETH as collateral we need to be able to borrow one DVT token
  4. We calculate how much token we can borrow from the pool given the amount of ETH that we have in our balance
  5. We calculate how much we can really borrow (because the pool has a limited amount of DVT token inside, and we know that it would revert if we try to borrow more than the balance)
  6. And we call lendingPool.borrow to borrow all the available DVTs

Further reading on Oracles and Price manipulation

Disclaimer

All Solidity code, practices and patterns in this repository are DAMN VULNERABLE and for educational purposes only.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
StErMi

StErMi

#web3 dev + auditor | @SpearbitDAO security researcher, @yAcademyDAO resident auditor, @developer_dao #459, @TheSecureum bootcamp-0, @code4rena warden