#36092 [SC-Insight] Collateralizable Contracts May Retain Status Unconditionally
Submitted on Oct 19th 2024 at 00:52:12 UTC by @auditweiler for Audit Comp | Anvil
Report ID: #36092
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)
Unbounded gas consumption
Description
Brief/Intro
Once assigned the `collateralizableContract` role, this contract can prevent retain the protocol administrator from ever removing the role.
Vulnerability Details
The role of the `collateralizableContract` within the `CollateralVault` is extremely valuable, as it provides the elevated permission to interact with user collateral reservations.
Through the use of a Return Bomb Attack, a `CollateralizableContract` can exploit the ERC-165 `supportsInterface(bytes4)` call to enact denial of service on the protocol `owner()` whenever they attempt to remove this status:
```solidity function upsertCollateralizableContractApprovals( CollateralizableContractApprovalConfig[] calldata _updates ) external onlyOwner { for (uint256 i = 0; i < _updates.length; i++) { address contractAddress = _updates[i].collateralizableAddress; if (contractAddress == address(0)) revert InvalidTargetAddress(contractAddress); collateralizableContracts[contractAddress] = _updates[i].isApproved;
@> } catch (bytes memory) { @> // contractAddress does not implement IERC165. `isCollateralPool` should be false in this case @> }
} ```
Notice that in the `catch` clause of the call to `supportsInterface`, the `bytes memory` variable remains. Although the variable is unnamed, this implementation specifically instructs the Solidity compiler to load the entirety of the returned data into memory, even though it is unused.
Consequently, a malicious contract can force the call to `upsertCollateralizableContractApprovals` to `revert` by specifying an execessively large amount of data to return from within the parent call context.
This renders the `upsertCollateralizableContractApprovals` function impotent, enabling the `collateralizableContract` to retain it's role.
Impact Details
The valuable `collateralizableContract` role can be held indefnitely. In the context of a batch upgrade, a malicious `CollateralizableContract` may also prevent competing `collateralizableContract`s from being approved.
As the `CollateralVault` itself is immutable, the role will be held indefinitely.
References
https://etherscan.io/address/0x5d2725fdE4d7Aa3388DA4519ac0449Cc031d675f?utm_source=immunefi#code
Proof of Concept
Proof of Concept
In Foundry, run the following test using:
```shell forge test -vv ```
> Note, you'll need to define a mainnet `ETH_RPC_URL`.
```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
/// @notice A contract which DOSs the `CollateralVault` if /// @notice it attempts to no longer mark them as a /// @notice `CollateralizableContract`. contract ImmortalCollateralizable { address internal immutable _COLLATERAL_VAULT;
}
contract ImmortalCollateralizableTest is Test { struct CollateralizableContractApprovalConfig { address collateralizableAddress; bool isApproved; }
} ```
This should result in the following output, demonstrating the `collateralizableContract` role can be retained:
```shell Ran 1 test for test/Counter.t.sol:ImmortalCollateralizableTest [PASS] testImmortalCollateralizable() (gas: 149659) Logs: Approval status assignment succeeded! Approval status removal failed!
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89s (488.07ms CPU time)
Ran 1 test suite in 2.05s (1.89s CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests) ```