#45514 [SC-Medium] malicious agents can trap stakers by raising the exit collateral ratio

#45514 [SC-Medium] Malicious agents can trap stakers by raising the exit collateral ratio

Submitted on May 15th 2025 at 23:58:30 UTC by @escrow for Audit Comp | Flare | FAssets

  • Report ID: #45514

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/flare-foundation/fassets/blob/main/contracts/assetManager/library/Agents.sol

  • Impacts:

    • Permanent freezing of funds

    • Temporary freezing of funds

    • Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)

Description

Brief/Intro

The Flare team implements the function Agents.sol::setPoolExitCollateralRatioBIPS() in order to change the exit collateral ratio of a pool.

Unfortunately, the current logic is flawed and allows a malicious agent to raise the pool exit collateral ratio higher to than what is allowed, and what is meant to be within range. This is due to a faulty require statement implementation. As a result, they have the ability to trap stakers or constantly rise the BIPS above normal limits compared to other users.

Vulnerability Details

The logic is safe and sound when we are firstly creating an agent vault and initialising it via the same function.

On an already initialised agent vault we execute the following flow:

AgentSettingsFacet.sol::announceAgentSettingUpdate() -> AgentSettingsUpdater.sol::announceUpdate() -> AgentSettingsUpdater.sol::executeUpdate() -> AgentSettingsUpdater.sol::_executeUpdate() -> Agents.sol::setPoolExitCollateralRatioBPS()

The updates are done by using a hash and then based on the hash value we execute the correct operation.

In the event that we are tweaking the exitCollateralRatioBIPS variable, we will execute the function setPoolExitCollateralRatioBPS().

Inside of this function, we have the main check which is responsible for ensuring that we are not too low and not too high.

As such, Flare invoke the following require statement:

require(_poolExitCollateralRatioBIPS <= currentExitCR * 3 / 2 || _poolExitCollateralRatioBIPS <= minCR * 12 / 10, "increase too big");

This require statement is problematic because we are using the || operators here, and thus the require statement works in a manner of an OR operation.

These two statements are critical as they serve two different purposes, but they are used together which is where the problem arises. The current statement means that only one has to be true, in order for the entire require statement to rule out true.

A malicious agent can take advantage of this because the currentExitCR is utilised and compared to the variable we are changing it to. We have room for leverage here in respect of the currentExitCR versus currentExitCR * 3 / 2. A delta example is given in the PoC based on the scenario.

Ultimately, we can keep forcing changes in respect of the delta zone here which means we never have to depend on succeeding the second statement of the overall require statement. So, we bypass the maxCR (so to speak) which is calculated by minCR * 12 / 10.

As a result, agents have unwarranted control over the natural mechanism of a collateral pool and have significant leverage over stakers where they can disallow them from exiting.

Such scenario is relevant to the functionality of CollateralPool.sol::exitTo or exit where within _exitTo we meet the following statement:

require(_staysAboveCR(assetData, natShare, assetData.exitCR), "..."); where assetData.exitCR is retrieved via _getAssetData() (which retrieves the exit collateral ratio via _safeExitCollateralRatioBIPS()). The staker has to meet the conditions of the system in respect of the exitCR value.

Impact Details

Malicious agents would be able to trap stakers within certain collateral pools unfairly and the stakers would not be able to correctly exit due to the inflated requirements.

Additionally, the malicious agents have the ability to simply bypass the margins of what BIPS they can work with, having an advantage over other agents with other collateral pools who abide by the rules.

If stakers are being griefed/trapped, then there is no way to guarantee that they will be aware of the grief/trap. Also, you can still force stakers to deal with higher amounts than usual just from 1 change due to the bypass of the second statement. Then, we have another scenario of an agent for instance purposefully bypassing the thresholds they should be abiding to even in normal situations with no malicious intent.

References

Already made within the vulnerability details.

Proof of Concept

Proof of Concept

I am aware only a step by step PoC is required, but I thought I'd attach the following anyway as you can see the logic in Chisel (Foundry).

➜ uint minCR = 4000;
➜ uint currentExitCR = 5500
➜ uint $ = 7000; /* _poolExitCollateralRatioBIPS */
➜ uint maxCR = minCR * 12 / 10;
➜ bool($ <= currentExitCR * 3 / 2)
Type: bool
└ Value: true
➜ bool ($ <= minCR * 12 / 10)
Type: bool
└ Value: false
➜ bool($ <= currentExitCR * 3 / 2 || $ <= minCR * 12 / 10)
Type: bool
└ Value: true
➜ uint delta = (currentExitCR * 3 / 2) - currentExitCR
➜ delta
Type: uint256
├ Hex: 0xabe
├ Hex (full word): 0x0000000000000000000000000000000000000000000000000000000000000abe
└ Decimal: 2750

Due to the above logic, all we have to do is just stay within the bounds of $ <-> currentExitCR * 3 / 2 and we can continue raising the CR required to leave, and as a result make it impossible to leave.

You cannot do a significant BIPS change straight away, but you can gradually climb there based on the delta of the statement of what you are allowed to change to. This increases every time due to how we increase upon every change.

Excluding the griefing stakers context, we still have a problem here regarding bypassing the minCR * 12 / 10 threshold which should not be allowed.

Was this helpful?