The Standard

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

`addUniqueHolder()` Looping Through `holders` Array for Stakers' Positions Poses a Potential Denial of Service (DoS) Attack

Description:
There is a potential DoS attack risk in the increasePosition() function due to the addUniqueHolder() method iterating over unbounded array lengths of holders, When a user wants to increase their position.


Impact:
If the holders array becomes excessively long, leading to an unresponsive state due to an Out of Gas error, users' funds will be permanently locked in the contract. This could mean death for the protocol as new liquidity can't enter the system.


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 holders 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 "testPositionIncrementDoS" -vvv
function testPositionIncrementDoS() public {
vm.txGasPrice(1);
// //////// Create one vault ////////
ISmartVault[] memory vaults = new ISmartVault[](20);
vaults = createVaultOwners(20);
//////// Owner 1 variables ////////
ISmartVault vault1 = vaults[0];
address owner1 = vault1.owner();
uint256 tstBalance1 = TST.balanceOf(owner1);
uint256 euroBalance1 = EUROs.balanceOf(owner1);
//////// Stake Tokens ////////
vm.warp(block.timestamp + 2 days);
vm.startPrank(owner1);
TST.approve(pool, tstBalance1);
EUROs.approve(pool, euroBalance1);
uint256 gasStart1 = gasleft(); // see gas cost for subsequent transaction
liquidationPool.increasePosition( tstBalance1/2, euroBalance1/2);
uint256 gasEnd1 = gasleft();
vm.stopPrank();
vm.warp(block.timestamp + 2 days);
uint256 gasUsed1 = (gasStart1 - gasEnd1) * tx.gasprice;
//////// Add more Holders to liquidationPool ////////
for(uint256 i = 1; i < vaults.length; i++){
vm.startPrank(vaults[i].owner());
TST.approve(pool, tstBalance1);
EUROs.approve(pool, euroBalance1);
liquidationPool.increasePosition( 1, 1);
vm.stopPrank();
}
vm.warp(block.timestamp + 2 days);
//////// Decrease 2nd half Position ////////
vm.startPrank(owner1);
TST.approve(pool, tstBalance1);
EUROs.approve(pool, euroBalance1);
uint256 gasStart2 = gasleft();
liquidationPool.increasePosition( tstBalance1 / 2, euroBalance1 / 2);
uint256 gasEnd2 = gasleft();
vm.stopPrank();
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.