# Damn Vulnerable DeFi 6 - 10 Solutions

By [kyrers](https://paragraph.com/@kyrers) · 2022-10-05

---

In this post, I will share my explanations for DamnVulnerableDeFI challenges 6 through 10.

You can find the code [here](https://github.com/kyrers/damn-vulnerable-defi).

Let’s begin.

Selfie
------

As per usual, this challenge involves a pool providing flash loans of 1.5 million `DVT` tokens. Our goal is to take them all.

Fortunately, we can immediately see that the pool has a `drainAllFunds(...)` function, which sends all pool `DVT` tokens to the `address` passed as a parameter. Unfortunately for us, it can only be called by governance.

`SelfiePool` is the first flash loan provider that has a governance mechanism attached to it, which, as stated in the challenge description, will be our way in. Looking into it we see that the `SimpleGovernance` contract allows for `actions` to be queued, provided they have enough votes. `_hasEnoughVotes()` checks that the `msg.sender` `DVT` balance is higher than half of the pool token balance.

`SelfiePool` also allows for an `action` to be executed, provided it has never been executed before and two days have passed since being `queued`. During a given `action` execution, the `action.receiver` will be called using the `action.data` and `action.weiAmount` as the `call` function parameters.

While looking through the `SimpleGovernance` contract, you'll hopefully have noticed that there are practically no access control mechanisms in place. This means that, as long as we can bypass `queueAction(...)` and `executeAction(...)` verifications, we are able to queue an `action` that will call the `SelfiePool` `drainAllFunds(...)` function and send them to us. It will work because the call is coming from governance.

To bypass `queueAction(...)` we need to have more than 750k `DVT` tokens at the time of the last `snapshot`. This is new - the pool implements an `ERC20Snapshot` version of the `DVT` token. Fortunately for us, anyone can take a `DVT` snapshot.

However, we are only interested in taking a `snapshot` when we do have enough `DVT` tokens, which we can get by implementing a contract that gets a `SelfiePool` flash loan.

All that's left is finding a way to bypass `executeAction(...)`, which is easily done by waiting two days!

So, one way to solve this challenge is to:

1.  Implement a contract that is able to get a flash loan from the `SelfiePool` contract;
    
2.  After receiving the flash loan, take a `snapshot` and queue an `action` with:
    
    *   `data` to make a call to the `drainAllFunds(...)` with our address as a parameter;
        
    *   `receiver` as the `SelfiePool` address;
        
    *   `weiAmount` must be 0, unless you funded the attacker contract;
        
3.  Pay the flash loan back;
    
4.  Wait two days;
    
5.  Execute the action.
    

Compromised
-----------

To me, the hardest challenge so far. I had to use some guessing to get this to work. I'll explain my rationale below.

The challenge setup is simple: We have an `Exchange` selling and buying `DVNTF` tokens at a `median price` obtained from averaging 4 `TrustfulOracle` price feeds. The goal is simple as well: Drain `Exchange` using only our 0.1 ETH.

Looking at the contracts, I did not notice any obvious exploit. The only possible way to drain the `Exchange` contract that I found consisted of manipulating the `DVNFT` price. To that effect, the challenge description came in handy. This is where the guessing started.

Since it is called "Compromised" I assumed the leaked information from the `HTTP` response would contain some private keys.

I looked at the leaked data and decided to convert it to a `string`. Unfortunately, that did not result in a private key. Eventually, it occurred to me that the `string` I obtained may very well be encoded, as it is an `HTTP` response. I used google and found out that often `HTTP` response data is `base64` encoded. So I converted the leaked data `string` using `base64`. At last, something that resembles a private key. I created wallets using the decoded leaked data and was happy to see that they corresponded to two trusted oracle addresses.

After that, it was just a question of manipulating the price using the compromised oracles.

Here's every step:

1.  Decode the leaked data and obtain two private keys;
    
2.  Create 2 wallets using the private keys obtained;
    
3.  Set the price to an affordable value. I used `0.005` ETH;
    
4.  Buy one `DVNFT` using the `attacker` account;
    
5.  Set the price to equal `Exchange` ETH balance;
    
6.  Sell the `attacker` `DVNFT`;
    
7.  Reset the price to the original value;
    

Puppet
------

This challenge is quite easy.

There is a `PuppetPool` that allows users to borrow `DVT` tokens, as long as the user deposits double that amount in `ETH` as collateral. The pool has a balance of `100000 DVT`. Our goal is to get our hands on all of them.

The pool uses an `Uniswap V1 DVT/ETH` pair to calculate the collateral needed. This pair starts with `10 ETH / 10 DVT` liquidity.

Lastly, our `attacker` starts with a balance of `25 ETH` and `1000 DVT`.

Since there are no flash loans, our best option is to somehow lower the collateral needed to borrow the `100000 DVT` tokens from the `PuppetPool`.

Looking at how the price is calculated we see the following equation `amount * _computeOraclePrice() * 2 / 10 ** 18;`.

Looking at the `_computeOraclePrice()` we see that the equation used is `uniswapPair.balance * (10 ** 18) / token.balanceOf(uniswapPair);`. This is problematic, because if the `Uniswap V1` exchange has a sufficiently larger balance of `DVT` tokens than `ETH`, the price to borrow tokens will decrease significantly.

Luckily for us, our attacker has `1000 DVT` tokens, so if we deposit these to the `Uniswap V1` exchange, our `25 ETH`, plus whatever `ETH` we receive from depositing to the `Uniswap V1` exchange, will be more than enough to borrow the `PuppetPool` `100000 DVT` tokens.

The only detail we must not forget is that to pass the challenge the attacker `DVT` balance must be higher than the initial `100000 DVT` `PuppetPool` balance. This means we cannot deposit all of the attackers `1000 DVT` tokens to the `Uniswap V1` exchange.

To recap:

1.  As the attacker, deposit an amount of `DVT` tokens large enough to lower the borrowing price to acceptable levels but less than 1000;
    
2.  Determine the amount of `ETH` needed as collateral to borrow the `PuppetPool` `100000 DVT` tokens;
    
3.  Borrow them.
    

It's this simple.

Puppet V2
---------

The set up of this challenge is very similar to the previous one.

The major differences are:

1.  The `PuppetV2Pool` now uses an `Uniswap V2` exchange to calculate the price;
    
2.  `WETH` is used instead of `ETH`;
    
3.  We are now required to deposit triple the borrow amount in `WETH` as collateral;
    

Despite the challenge description mentioning that developers learned from the original `Puppet` implementation, and in fact this is a more secure implementation, the same problem remains.

The `Uniswap V2` pair used to calculate the borrowing price still has low liquidity, meaning that users with sufficient amounts of either of the pair tokens can affect significant price swings.

Luckily for us, our `attacker` has a significant amount of `DVT` tokens, meaning we can swing the price in our favor once again.

So, the steps to solve this challenge are similar to the previous one:

1.  Swap the `attacker` `DVT` tokens for `ETH` using the `Uniswap V2` pair;
    
2.  Convert the `attacker` `ETH` to `WETH`;
    
3.  Borrow all `PuppetV2Pool` `DVT` tokens;
    

Free Rider
----------

The set up of this challenge consists of a `FreeRiderNFTMarketplace` that is selling 6 NFTs for `15 ETH` each. We also have a `FreeRiderBuyer` which told us that the marketplace is exploitable, and if we exploit it and send them the 6 NFTs, it will pay us `45 ETH`. This is our goal.

The challenge description mentions that we start with `0.5 ETH` - which is clearly not enough to buy the 6 NFTs. It also implies that there's a way to get some more ETH. If we look at the script that sets up the challenge, we can see that there's an `Uniswap V2 WETH/DVT` pair.

First things first, we need to find the exploit in the `FreeRiderNFTMarketplace`. Looking at the contract we can see that we can only call the `buyMany(...)` function to buy NFTs. This function calls the `_buyOne(...)`, which is the function that actually handles the purchase. The exploit must be here.

Luckily for us, it is easy to find what's wrong with this function - it is the way that it checks the price we have to pay. Suppose we call the `buyMany(...)` function with the ids of the 6 NFTs, the `require(msg.value >= priceToPay, "Amount paid is not enough")` check is wrong because it is only checking the price of one NFT, not the sum of all 6. This means that for the price of one NFT, `15 ETH`, we can buy all six!

As a bonus, there's also another bug. It sends the `15 ETH` to `token.ownerOf(tokenId)`, the owner of the NFT. However, before doing so it transfers the NFT to the buyer - meaning that not only is `15 ETH` enough to buy all 6 NFTs, we actually get the `90 ETH` we were supposed to pay, because we are the new owner.

All that's left is figuring out how to get the `15 ETH` we need. For that, you'll need to look into the `Uniswap V2` documentation. This is the difficult part of the challenge.

You'll hopefully find that we can take a `flashswap` from the `WETH/DVT` pair, which we will be able to payback easily due to our expected profit from the attack.

We will need to perform this attack using a contract, not only because we need to implement a `uniswapV2Call(...)` function to receive the `flashswap`, but also because we need an `onERC721Received` to receive the NFTs.

Here's the full attack:

1.  Get a `flashswap` of `15 WETH`;
    
2.  Unwrap that `WETH`;
    
3.  Buy the 6 NFTs;
    
4.  Send them to the buyer;
    
5.  Pay the `flashswap` back;
    
6.  Send the all profits to the `attacker` account;

---

*Originally published on [kyrers](https://paragraph.com/@kyrers/damn-vulnerable-defi-6-10-solutions)*
