📄

Report #12593

Report Date
October 20, 2022
Status
Closed
Payout

Critical: Take funds by changing Fee Receiver Address

‣
Report Info

Report ID

#12593

Target

Report type

Smart Contract

Impacts

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

Has PoC?

Yes

Details

Missing access control allows to change the fee receiver address on admin contract.

Vulnerable Admin Contract

Fix

Add this line of code

    assert msg.sender == self.parameter_admin

Proof of concept

Visit here

  1. https://etherscan.io/token/0xc9c32cd16bf7efb85ff14e0c8603cc90f6f2ee49#readContract
  2. It has admin https://etherscan.io/address/0x8CF8Af108B3B46DDC6AD596aebb917E053F0D72b#writeContract
  3. Check this function
@external
def set_fee_receiver(_target: address, _base_pool: address, _fee_receiver: address):
    Factory(_target).set_fee_receiver(_base_pool, _fee_receiver)
  1. This function missed the owner check, allows anyone to set the fee receiver.

Exploit

  1. On this contract https://etherscan.io/address/0x8CF8Af108B3B46DDC6AD596aebb917E053F0D72b#writeContract

Attacker will call set_fee_receiver and change the fee receiver.

@external
def set_fee_receiver(_target: address, _base_pool: address, _fee_receiver: address):
    Factory(_target).set_fee_receiver(_base_pool, _fee_receiver)
  1. Then Attacker will call withdraw_admin_fees on this contract https://etherscan.io/token/0xc9c32cd16bf7efb85ff14e0c8603cc90f6f2ee49
def withdraw_admin_fees():
    factory: address = self.factory

    # transfer coin 0 to Factory and call `convert_fees` to swap it for coin 1
    coin: address = self.coins[0]
    amount: uint256 = ERC20(coin).balanceOf(self) - self.balances[0]
    if amount > 0:
        ERC20(coin).transfer(factory, amount)
        Factory(factory).convert_fees()

    # transfer coin 1 to the receiver
    coin = self.coins[1]
    amount = ERC20(coin).balanceOf(self) - self.balances[1]
    if amount > 0:
        receiver: address = Factory(factory).fee_receiver(BASE_POOL)
        ERC20(coin).transfer(receiver, amount)
  1. As you can see the code
Factory(factory).fee_receiver(BASE_POOL)
ERC20(coin).transfer(receiver, amount)
  1. It transfers funds to fee recieved address which has been already modified by attacker above.
  2. Finally attacker get all funds lying in the smart contract which are about $13996054 BEAN and $13673098 CURVEFI 3CRV.

BIC Response

This submission is related to an out of scope asset: the BEAN:3CRV Curve LP token. Curve pools are not part of Beanstalk and thus not included in the Immunefi bug bounty program. Curve pools are also non-upgradable.

The Beanstalk DAO acknowledges the risk of using Curve and has transparently communicated that here:

Due to these reasons, this report is not eligible for a reward.