Submitted on Nov 5th 2024 at 15:07:44 UTC by @Blockian for
Report ID: #36540
Report Type: Smart Contract
Report severity: Insight
Target: https://etherscan.io/address/0x5d2725fdE4d7Aa3388DA4519ac0449Cc031d675f
Impacts:
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Protocol insolvency
Not paying proper fees -> Motive for an attacker + damage to the protocol
A vulnerability exists in the `CollateralVault.sol` contract, allowing users to withdraw funds at any historical fee rate, even after the protocol has increased fees.
When users withdraw funds, they are required to pay the current `withdrawalFeeBasisPoints` percent as a fee to the protocol. However, to preserve fee conditions for pre-existing reservations, the protocol applies the fee rate in effect at the time each reservation was created.
Typically, only the Collateralizable Contract should be authorized to interact with reservations. This restriction is generally enforced through a `is _collateralizableAddress in collateralizableContracts` check. However, an exception in the `_requireCollateralizableAndDecreaseApprovedAmount` function creates an unintended loophole:
```javascript function _requireCollateralizableAndDecreaseApprovedAmount( address _collateralizableAddress, address _accountAddress, address _tokenAddress, uint256 _amount ) internal { if (_collateralizableAddress == _accountAddress) { return; }
} ```
When `_collateralizableAddress == _accountAddress`, no check confirms that `_collateralizableAddress` is an approved Collateralizable Contract. This oversight allows a user to exploit `_requireCollateralizableAndDecreaseApprovedAmount`, treating their own address as a Collateralizable Contract and manipulating the reservation process.
Consequently, users can initiate, modify, and claim collateral from reservations, using historical fees based on historical rates instead of the current protocol rate.
Initial Setup: Alice deposits `x` funds as collateral by calling `depositToAccount`.
Reservation Creation: Alice creates a reservation with `amount = 1` using `reserveClaimableCollateral`.
From here, Alice can choose a strategy based on fee changes.
If Fees Increase
Alice modifies her reservation to hold her entire balance via `modifyCollateralReservation`.
Alice withdraws her funds at the lower, historical fee rate by calling `claimCollateral`.
If Fees Decrease
Alice cancels the reservation with `releaseAllCollateral`.
Alice has two options:
Option 1: Create a new reservation with `amount = 1` to "lock in" the lower fee rate in case fees rise in the future.
Option 2: Withdraw her funds directly at the current rate.
This vulnerability allows users to exploit historical fee rates, undermining the protocol's fee structure and potentially resulting in revenue loss for the protocol.
To resolve this issue, users cannot act as their own Collateralizable Contracts. Modifying the `_requireCollateralizableAndDecreaseApprovedAmount` function to perform the `collateralizableContracts` check first would close this loophole:
```javascript function _requireCollateralizableAndDecreaseApprovedAmount( address _collateralizableAddress, address _accountAddress, address _tokenAddress, uint256 _amount ) internal { if (!collateralizableContracts[_collateralizableAddress]) { revert ContractNotApprovedByProtocol(_collateralizableAddress); // Enforce contract verification first. }
} ```
Run this foundry test on a fork of the mainnet
This is the command I used to run the test `forge test --fork-url https://eth-mainnet.g.alchemy.com/v2/$ALCHEMY_KEY --fork-block-number 21121956 --gas-price 0 --via-ir -vvvv`
NOTE: Make sure to add the `setWithdrawalFeeBasisPoints` and `depositToAccount` function signatures to the `ICollateral` interface before running.
```js // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
import "forge-std/console.sol"; import {Test} from "forge-std/Test.sol"; import {ICollateral} from "../src/ICollateral.sol"; import {WETH} from "../src/WETH.sol";
contract MainTest is Test { ICollateral public collateral; WETH public weth;
} ```