Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
c4-bot-3 opened this issue Aug 19, 2024 · 0 comments
Open
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working 🤖_primary AI based primary recommendation 🤖_52_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality

Comments

@c4-bot-3
Copy link
Contributor

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

@c4-bot-3 c4-bot-3 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Aug 19, 2024
c4-bot-5 added a commit that referenced this issue Aug 19, 2024
@c4-bot-12 c4-bot-12 added 🤖_52_group AI based duplicate group recommendation 🤖_primary AI based primary recommendation labels Aug 19, 2024
howlbot-integration bot added a commit that referenced this issue Aug 20, 2024
@howlbot-integration howlbot-integration bot added the sufficient quality report This report is of sufficient quality label Aug 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working 🤖_primary AI based primary recommendation 🤖_52_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

2 participants