code-423n4 / 2024-07-reserve-findings

1 stars 0 forks source link

The `rateLimiter` functionality in the `Throttle.sol` contract can be bypassed #60

Closed howlbot-integration[bot] closed 1 month ago

howlbot-integration[bot] commented 1 month ago

Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/libraries/Throttle.sol#L37-L65

Vulnerability details

Impact

The rateLimiter functionality in the Throttle.sol contract can be bypassed. The issuance rate limiter can be bypassed by sandwiching the redemption transaction with two issuance transactions by a issuer.

Let's consider the following scenario:

hourly issuance rate is 200
hourly redemption rate is 300

Consider the lastAvailabe amounts are as follows:

issuanceThrottle.lastAvailable is 200
redemptionThrottle.lastAvailable is 300

Now let's consider the following transaction execution:

1. The User B decides to call the redmption of 300 RTokens.

The current state of respective lastAvailable amounts are as follows:
issuanceThrottle.lastAvailable is 200
redemptionThrottle.lastAvailable is 300

2. User A sees the above transaction and front runs it by calling the issuance for 100 RTokens.

The current state of respective lastAvailable amounts are as follows:
issuanceThrottle.lastAvailable is 100
redemptionThrottle.lastAvailable is 400

3. Now the User B's redemption transaction of 300 RTokens is executed.

The current state of respective lastAvailable amounts are as follows:
issuanceThrottle.lastAvailable is 400
redemptionThrottle.lastAvailable is 0

4. Now the User A sanwitches User B's redemption transaction by calling the issuance of 200 RTokens.

The current state of respective lastAvailable amounts are as follows:
issuanceThrottle.lastAvailable is 0
redemptionThrottle.lastAvailable is 200

In this scenario, User A was able to issue a total of 300 RTokens (100 + 200) within a single block, effectively bypassing the hourly issuance rate limit of 200.

The reason this exploit works is due to the way the Throttle.useAvailable function updates the lastAvailable values for issuance and redemption. When a redemption occurs, the issuanceThrottle.lastAvailable value is increased by the redemption amount, allowing for a higher issuance amount.

This behavior can be exploited by alternating between issuance and redemption operations, effectively accumulating a higher available amount for issuance than the configured hourly rate limit as explained in the previous example.

Proof of Concept

    function useAvailable(
        Throttle storage throttle,
        uint256 supply,
        int256 amount
    ) internal {
        // untestable: amtRate will always be > 0 due to previous validations
        if (throttle.params.amtRate == 0 && throttle.params.pctRate == 0) return;

        // Calculate hourly limit
        uint256 limit = hourlyLimit(throttle, supply); // {qRTok}

        // Calculate available amount before supply change
        uint256 available = currentlyAvailable(throttle, limit); //@audit-info - get teh avialbel amount

        // Update throttle.timestamp if available amount changed or at limit
        if (available != throttle.lastAvailable || available == limit) {
            throttle.lastTimestamp = uint48(block.timestamp);
        } //@audit-info - update teh last timestamp

        // Update throttle.lastAvailable
        if (amount > 0) {
            require(uint256(amount) <= available, "supply change throttled");
            available -= uint256(amount);
            // untestable: the final else statement, amount will never be 0
        } else if (amount < 0) {
            available += uint256(-amount);
        }
        throttle.lastAvailable = available; //@audit-info - update the throttle last available amount
    }

https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/libraries/Throttle.sol#L37-L65

Tools Used

VSCode and Manual Review

Recommended Mitigation Steps

To mitigate this vulnerability, the Throttle.sol contract should be modified to prevent the issuanceThrottle.lastAvailable value from increasing beyond the configured hourly issuance rate limit, regardless of any redemption operations. One possible solution could be to introduce separate checks or constraints to ensure that the lastAvailable values for issuance and redemption are capped by their respective hourly rate limits.

Assessed type

Other

akshatmittal commented 1 month ago

This throttle is about net supply change. In your example, there's a user redeeming and another user is issuing the RTokens, however the net result remains within the throttle.

c4-judge commented 1 month ago

thereksfour marked the issue as unsatisfactory: Invalid