#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) ```

Last updated

Was this helpful?