# Selfie - Damn Vulnerable DeFi #06

## Objectives

There's a pool as always, and it offers flash loans of DVT tokens. There's also a governance mechanism that controls the pool.

The initial token supply is 2 million, and the pool has 1.5 million DVT. We have 0. Our goal is to drain the pool. Let's get started.

## Smart Contract Analysis

**SelfiePool.sol**

%[https://gist.github.com/az0mb13/76c4ab4151d7c58458da3589515df9e5] 

The DVT token being used is an [ERC20 Snapshot](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Snapshot.sol) token that can be used to take snapshots at a certain point in time to record the balances.

There are two important functions here:

* `flashLoan()` is the same thing that we have seen in the last few contracts, users can take flash loans for free, it calls a `receiveTokens()` function in their contract to handle the tokens and repay them back to the pool.
    
* `drainAllFunds()` drains all the tokens from the pool which we need to do, but notice the `onlyGovernance` modifier, it only allows the Governance contract (`address(governance)`) to call the function. This gives us a hint that we somehow need to make the Governance contract call this function for us.
    

---

**SimpleGovernance.sol**

%[https://gist.github.com/az0mb13/a1ff0a4a0a9421dd7aca3d4dfef95563] 

Let's now take a look at the Governance contract. There are two external functions that can be called by anyone:

* `queueAction()` : This function is used to propose actions/changes to the contract if enough votes allow them to. to check enough votes, the function calls:
    
    * `_hasEnoughVotes()` : This function fetches the balance of the last snapshot assuming a snapshot is already taken, calculates the total token supply at the last snapshot, and compares both of them. It returns true if the caller's balance is greater than `total token supply / 2`. This means the caller must own at least `1 million + 1 token` to bypass this check. (Since the token's initial supply is 2 million)
        
* The data is stored in a struct and an `actionId` is returned.
    
* `executeAction()` : Once the action has been proposed and stored, this function is used to execute the action based on the `actionId` that was returned.
    
    * It calls `_canBeExecuted()` which makes sure that the action has not already been executed, and that there's a sufficient delay after proposing the action. (2 days)
        
* Once all the validations pass, it makes a `functionCallWithValue()` with the bytes data that we stored in the action along with some Wei to the receiver's address which we also stored in the action.
    

## The Exploit

The above analysis shows that if we can get our action to store and execute, we should be able to make the governance contract make an external call to the `SelfiePool` and make it drain the tokens for us. We can always take a flash loan from the pool to get enough tokens to pass the validation in `_hasEnoughVotes()`. Let's write the attacker's contract.

%[https://gist.github.com/az0mb13/ec8411ce67aedee58ccd5e46c72ab9d3] 

After initializing all the addresses, we have defined 3 functions:

* `attack()` : This is responsible for triggering the `flashLoan()` with at least 1 million + 1 token. We'll just take a loan for the complete amount in the pool, i.e., 1.5 million DVT.
    
* The flow comes back to our other function, `receiveTokens()` where the attack starts:
    
    * `ITokenSnapshot(token).snapshot();` : We have to first take a snapshot because otherwise, the transaction will fail inside `_hasEnoughVotes()` since it needs an already created snapshot to fetch balances from.
        
    * We are calling `gov.queueAction` with the `receiver` as the pool's address, since it'll be making a function call to the pool to drain the tokens. The second parameter is the bytes encoded data: `abi.encodeWithSignature("drainAllFunds(address)", attacker)`. It contains the encoded function signature with the argument as the attacker's address since we need to get all the funds to our attacker from the pool.  
        The `weiAmount` can be 0 since it's not needed by the `drainAllFunds()` function.  
        The return value is stored in a storage parameter `actionId` because it'll be needed again to execute the action.
        
    * Once we are able to propose the action, we transfer the tokens back to the pool on Line 54 since we took a flash loan.
        
* After 2 days have passed, we can make a call to the `execute()` function to call `executeAction()` which executes the action, drains all the pool funds, and transfers them to the attacker's address.
    

Here's how the test case looks:

%[https://gist.github.com/az0mb13/7ee5289da0c68e89b3bbeac335142c73] 

We deploy the attacker's contract and call the `attack()` function to borrow `TOKENS_IN_POOL` (1.5 million) DVT tokens.

We then use `evm_increaseTime` to fast forward 2 days and then call the `execute()` function to finish the exploit.

Run the script using `yarn run selfie` and the test case will pass.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674931593463/d6953e73-7b0d-44c8-ab1b-c699e96d21a2.png align="center")

## **Key Takeaways**

There are multiple issues with these contracts. The key ones are:

* Critical functions such as voting proposals should account for tokens coming through flash loans.
    
* The function to create snapshots should be behind proper access controls.
    
* Creating your own Governance requires a lot of thorough testing and diligence which is why it is recommended to use OpenZeppelin's [Governor](https://docs.openzeppelin.com/contracts/4.x/api/governance).
    

%[https://github.com/az0mb13/damn-vulnerable-defi/blob/master/contracts/selfie/SelfieExploit.sol]
