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

Report #44213

Report Date
April 18, 2025
Status
Closed
Payout

Improper ERC20 Handling in LibTransfer Enables Tokenless Deposits and Illegitimate Minting

‣
Report Info

Report ID

#44213

Report Type

Smart Contract

Has PoC

Yes

Target

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

Impacts

  • Illegitimate minting of protocol native assets
  • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
  • Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
  • Contract fails to deliver promised returns, but doesn't lose value

Details

The Beanstalk protocol uses a custom safeTransferFrom() function inside LibTransfer that fails to properly validate return values from ERC20 token transfers. This allows an attacker to use a malicious ERC20 token that omits the return value, leading the protocol to believe a transfer succeeded when in reality it may have failed or not occurred at all. As a result, the attacker can interact with functions like deposit() and receive protocol-native credits or rewards without actually transferring any tokens.

Vulnerability Details

According to the ERC20 standard, transferFrom() must return a boolean value indicating success. However, not all ERC20 tokens strictly follow this rule. In the current Beanstalk implementation, the function safeTransferFrom() does not properly decode or check this return value. Instead, it assumes success even if the transfer result is missing or malformed.

This opens the door for attackers to:

  • Deploy a custom ERC20 token where transferFrom() does not return a value.
  • Use this token in functions like deposit() or any function that invokes LibTransfer.safeTransferFrom().
  • Trick the protocol into recording a successful deposit, thus potentially receiving unearned rewards or triggering additional downstream logic.

Example malicious token:

function transferFrom(address from, address to, uint256 amount) public override returns () {
    _transfer(from, to, amount);
    // No return value at all
}

Impact Details

This vulnerability leads to false assumptions within the protocol's internal accounting. The attacker can use the above token to:

  • Falsely increase their deposited balance
  • Claim rewards or BEAN tokens without actually contributing value
  • Manipulate the state of the system or dilute the value of legitimate users' deposits

If chained with other logic that allows withdrawal or reward conversion, this can escalate into direct financial loss or inflation of the BEAN supply. At minimum, it represents a significant integrity flaw and opens the door to systemic abuse.

References

  • Affected main contract: https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5
  • ERC20 spec: https://eips.ethereum.org/EIPS/eip-20

Proof of Concept

  1. Deploy a malicious ERC20 token with a transferFrom() function that does not return a value (as shown below).
  2. Whitelist this token in the Beanstalk protocol (if needed, depending on integration).
  3. Call deposit() or any function that uses LibTransfer.safeTransferFrom() with the malicious token.
  4. Observe that the transaction succeeds and the protocol assumes tokens were transferred — even though they were not.
  5. Internal accounting is updated, and the attacker can receive credits or rewards without providing real value.

Malicious Token Contract

Why It Works

  • Beanstalk's LibTransfer.safeTransferFrom() fails to validate the return value of the transferFrom() call.
  • As a result, the protocol assumes success even when the token does not return true/false, allowing attackers to trick the system.
  • This creates a vector for:
    • Reward abuse
    • Fake deposits
    • Potential downstream exploits through inflated balances

Recommendation

Update safeTransferFrom() to validate the return value as follows:

(bool success, bytes memory data) = token.call(
    abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "Transfer failed");

BIC Response

The vulnerability you describe would only apply if malicious ERC20 tokens were whitelisted for deposit. Therefore we are closing this report and no reward will be issued.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract BadToken is ERC20 {
    constructor() ERC20("BadToken", "BAD") {
        _mint(msg.sender, 1_000_000 ether);
    }

    function transferFrom(address from, address to, uint256 amount) public override returns () {
        _transfer(from, to, amount);
        // No return value — violates ERC20 spec
    }
}