#36303 [SC-Medium] attackers can cause griefing attack to cause stake transactions of timebasedcolla
#36303 [SC-Medium] Attackers can cause griefing attack to cause stake transactions of TimeBasedCollateralPool of users to always revert by front-running the user transaction to make the provided si...
Submitted on Oct 28th 2024 at 15:29:49 UTC by @perseverance for Audit Comp | Anvil
Report ID: #36303
Report Type: Smart Contract
Report severity: Medium
Target: https://etherscan.io/address/0xd042C267758eDDf34B481E1F539d637e41db3e5a
Impacts:
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Description
Brief/Intro
The users can stake into the TimeBasedCollateralPool by using the function stake
```solidity /** * @inheritdoc ITimeBasedCollateralPool */ function stake( IERC20 _token, uint256 _amount, bytes calldata _collateralizableApprovalSignature ) external withEligibleAccountTokensReleased(msg.sender, address(_token)) returns (uint256) { if (_collateralizableApprovalSignature.length > 0) { collateral.modifyCollateralizableTokenAllowanceWithSignature( msg.sender, address(this), address(_token), Pricing.safeCastToInt256(_amount), _collateralizableApprovalSignature ); }
```
To trigger the _stake action, the collateralizable contract must have been granted a non-zero allowance by the user. The user can provide a signed message to increase the corresponding allowance and thus make the whole transaction possible.
If the signature is provided, then collateral.modifyCollateralizableTokenAllowanceWithSignature is called to check the signature.
The vulnerability
Vulnerability Details
An attacker may front-run a stake transaction containing a signed message and make it fail/revert.
The attacker front-runs the user's transaction and uses the signature to call modifyCollateralizableTokenAllowanceWithSignature successfully. The user's stake call fails because the signed message has already been used, thereby increasing the nonce.
This is possible because the function modifyCollateralizableTokenAllowanceWithSignature allows anyone to call.
```solidity function modifyCollateralizableTokenAllowanceWithSignature( address _accountAddress, address _collateralizableContractAddress, address _tokenAddress, int256 _allowanceAdjustment, bytes calldata _signature ) external { if (_allowanceAdjustment > 0 && !collateralizableContracts[_collateralizableContractAddress]) revert ContractNotApprovedByProtocol(_collateralizableContractAddress);
```
Impacts
About the severity assessment
By this attack, the attacker cause user's stake transactions always revert. This does not bring financial benefit to the attacker, but cause damage to the users and the protocol. The damage is the user's gas for failed transactions and Denial of Service and bad user experience. The damage for the protocol is bad user experience.
Bug Severity: Medium
Impact category: Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Difficulty of the attack: Easy
It is easy to automate the attack
Link to Proof of Concept
https://gist.github.com/Perseverancesuccess2021/5dd1ab799db0034872a7a436614092b8#file-testtimebasedcollateralpool-sol
Proof of Concept
Proof of concept
Steps to execute the attack:
Step 1: Victim call the stake function and provide the signature in the transaction
Step 2: Attacker monitors the mempool and front-run the user transaction by calling CollateralVault_contract.modifyCollateralizableTokenAllowanceWithSignature
So the user's transaction will revert
Test code to show: ```solidity function testStake() public {
```
Explanation:
The test case testStake() demonstrates the normal use-case when the victim call stake function to stake to the TimeBasedCollateralPool. This test case passes.
But in the testStake_hacked(), the attacker front-run the victim's transaction by calling modifyCollateralizableTokenAllowanceWithSignature. Then the victim's transaction reverts.
Test Logs: Log file:
https://gist.github.com/Perseverancesuccess2021/5dd1ab799db0034872a7a436614092b8#file-teststake_241028_2120-log
```Log No files changed, compilation skipped
Ran 2 tests for test/testTimeBasedCollateralPool.sol:testTimeBasedCollateralPool [PASS] testStake() (gas: 311392) Logs: Setup Precondition for the test case Deploying TimeBasedCollateralPool Proxy contract that is VisibleBeaconProxy points to TimeBasedCollateralPool_beacon Initialize TimeBasedCollateralPool contract Approve the TimeBasedCollateralPool_contract as the collateralizable contract Balance of USDC before depositing: 10000000000 Approve the CollateralVault_contract to spend USDC to deposit USDC Step: Deposit USDC to CollateralVault for the victim Victim stake to TimeBasedCollateralPool
[FAIL. Reason: InvalidSignature(0xe05fcC23807536bEe418f142D19fa0d21BB0cfF7)] testStake_hacked() (gas: 123221) Logs: Setup Precondition for the test case Deploying TimeBasedCollateralPool Proxy contract that is VisibleBeaconProxy points to TimeBasedCollateralPool_beacon Initialize TimeBasedCollateralPool contract Approve the TimeBasedCollateralPool_contract as the collateralizable contract Balance of USDC before depositing: 10000000000 Approve the CollateralVault_contract to spend USDC to deposit USDC Step: Deposit USDC to CollateralVault for the victim Attacker front-run the victim by calling modifyCollateralizableTokenAllowanceWithSignature of CollateralVault_contract Victim stake to TimeBasedCollateralPool
Suite result: FAILED. 1 passed; 1 failed; 0 skipped; finished in 298.62ms (2.69ms CPU time)
Ran 1 test suite in 306.36ms (298.62ms CPU time): 1 tests passed, 1 failed, 0 skipped (2 total tests)
Failing tests: Encountered 1 failing test in test/testTimeBasedCollateralPool.sol:testTimeBasedCollateralPool [FAIL. Reason: InvalidSignature(0xe05fcC23807536bEe418f142D19fa0d21BB0cfF7)] testStake_hacked() (gas: 123221)
Encountered a total of 1 failing tests, 1 tests succeeded
```
Full POC:
https://gist.github.com/Perseverancesuccess2021/5dd1ab799db0034872a7a436614092b8#file-testtimebasedcollateralpool-sol
```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0;
import "forge-std/Test.sol"; import "forge-std/console.sol"; import "../src/TimeBasedCollateralPool.sol"; import "../src/CollateralVault.sol"; import "../src/VisibleBeaconProxy.sol"; import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
contract testTimeBasedCollateralPool is Test {
} ```
Just download the zip file:
https://drive.google.com/file/d/1u6GpvBXKcHu_OBxBmMPtw-d2EirSsKtw/view?usp=sharing
The test code uses Foundry. Just Unzip and run the test case:
```bash forge test --match-test testStake -vvvvv > testStake_241028_2120.log
```