📄

Report #30817

Report Date
May 6, 2024
Status
Closed
Payout

User/Beanstalk ETH value can be drained by Malicious Actors

Report Info

Report ID

#30817

Report type

Smart Contract

Has PoC?

Yes

PoC Link

https://gist.github.com/Sentient-XII/2891fdfdf95b7de9e78c02b16a59462e

Target

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

Impacts

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

Description

Lack of reentrancy check in advanceFarmCall function can lead to loss of eth for beanstalk and also for victims with extra eth value attached to transfers of ERC115 to malicious contract.

Vulnerability Details

A similiar bug was reported by Cyfrin in an audit report, but the ability to steal eth value directly from beanstalk was not highlighted.

I am therefore submitting under the guidelines of immunefi: https://immunefisupport.zendesk.com/hc/en-us/articles/18327931722257-Audits-known-issues section that covers I found a bug that was uncovered in a project’s audit in the past, but wasn’t fixed. Can I report it?

In addition, over its lifetime beanstalk Diamond contract had over 10 eth and this the loss of this eth was highly possible. (see snapshot attached)

Basically, the function advanceFarmCall does not have reentrancy gaurd and therefore allows anyone transferring eth to re-enter beanstalk address and take any eth associated with the call including beanstalk eth balance if any.

Stealing eth from Victim:

  1. Victim calls advanceFarmCall to send ERC1155 to a malicious contract
  2. Malicious contract calls back into onERC1155Received and removes all ETH

Steal eth from beanstalk

  1. Beanstalk get's balance of ETH
  2. Malicious Actors sends 1 wei to himself
  3. Malicious actor reenters onERC1155Received and removes all eth balance.

Impact Details

  1. Any address that interacts with a malicious contract and has extra eth attached to the call and happens to call advanceFarmCall function the eth can be stolen.
  2. If for any reasons beanstalk holds a balance eth, (like in the past) it can directly be stolen

Although beanstalk does not have eth balance now, historically it had up to 10 ether. In addition, fertilizer is actively traded on OpenSea currently and users risk interacting with malicious actors.

References

AdvanceFarmCall function:

    function advancedFarm(AdvancedFarmCall[] calldata data)
        external
        payable
        withEth
        returns (bytes[] memory results)
    {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; ++i) {
            results[i] = _advancedFarm(data[i], results);
        }
    }

Proof of concept

Proof of Concept:

To illustrate the risk we deal beanstalk.Diamond contract 10 ether.

  1. mkdir beanPOC
  2. cd beanPOC
  3. forge init
  4. delete Counter.sol and Counter.t.sol and test folder
  5. place POC and interface in src folder

run with forge test --contracts ./src/beanAttack.sol -vv --evm-version shanghai

expected results:

Ran 1 test for src/beanAttack3.sol:ContractTest
[PASS] testFarm() (gas: 624995)
Logs:
  Exploiter balance before          : : 1
  BEANSTALK balance before          : : 10000000000000000000
  Exploiter balance before          : : 1
  Remaining Capitalization before   : : 38444145899132
  entered exploiter onERC1155Received
  Exploiter balance after           : : 0
  BEANSTALK balance after           : : 0
  Exploiter balance after           : : 10000000000000000002
  Remaining Capitalization after    : : 38411881899132

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 848.78ms (16.23ms CPU time)

https://gist.github.com/Sentient-XII/2891fdfdf95b7de9e78c02b16a59462e

BIC Response

advancedFarm does not have a reentrancy guard because the purpose of the function is to call multiple functions within Beanstalk in a single transaction. If advancedFarm had a reentrancy guard, the function would fail upon calling any nonReentrant function (rendering it effectively useless). Furthermore, the issue described in the report requires someone to send ETH directly to Beanstalk, which is considered user error.