Selfie

Description

A new cool lending pool has launched! It’s now offering flash loans of DVT tokens. It even includes a fancy governance mechanism to control it.

What could go wrong, right ?

You start with no DVT tokens in balance, and the pool has 1.5 million. Your goal is to take them all.

TL;DR

_hasEnoughVotes() checks governance token balance based on snapshot. We can call governanceToken.snapshot() directly since there is no access control. Just borrow flashloan and take snapshot in the callback, so that the contract thinks we have a lot of balance.

Code Audit

Snapshot can be buggy if there is no minimum "token locked time" required, which is the case for this chall.

To be able to queue actions, we must hold more than half of the governance token totalSupply:

    function _hasEnoughVotes(address account) private view returns (bool) {
        uint256 balance = governanceToken.getBalanceAtLastSnapshot(account);
        uint256 halfTotalSupply = governanceToken.getTotalSupplyAtLastSnapshot() / 2;
        return balance > halfTotalSupply;
    }

Two things here:

  1. The governanceToken is dvtSnapshot, which is the only token used in this chall. The condition checked by _hasEnoughVotes() can easily be achieved since dvtSnapshot is the token of the flashloan pool as well.

  2. Also, look at the DamnValuableTokenSnapshot.snapshot() function, this function has no access control so we can directly call it once we receive the flashloan:

At this our balance in the snapshot will be huge because of the flashloan.

Once we receive the flashloan, we can queue an action:

What action to queue though? Note that there is a function named SelfiePool.drainAllFunds()...Well that is clear enough:

After storing this action in the queue, we call SimpleGovernance.executeAction() to execute the action:

There a restriction though:

So we have to vm.warp() two days forward for calling SimpleGovernance.executeAction(). Since we can't vm.warp() within the attack contract of course, the idea is to do flashloan, queueAction(), and pay back flashloan in attack contract, then vm.warp() in test file and call executeAction() at the very end.

Building PoC

Last updated