Full Report
Beanstalk is a stablecoin protocol. In order to peg the price of BEAN, the function convertFacet() is used. When passing in token addresses for the stablecoin pool, there was no validation that the well address is valid. The function convert() takes in three parameters: convertData structure, an array of ints called stems and an array of int called amounts. When providing a list of stems and amounts, there is no validation that these are NOT zero length. I imagine that a loop contained some validations but didn't consider this case. If the convertData has a type of WELL_LP_TO_BEANS, it contains a well address. When using this, the well address not was verified to be an allowlisted value. This allows for an expected and trusted contract to be spoofed with arbitrary values. Later on, a call to _wellRemoveLiquidityTowardsPeg is made. This has a call to removeLiquidityOneToken on the well, which can return extremely small values. So, the convert function is made with a BEAN deposit without withdrawing any real tokens. Eventually, these can be claimed by an attacker through a different function call. There are two issues within the code. The first one is a lack of validation of the zero length arrays leads to several values being zero during the conversion. The second issue is with the missing validation on the trusted address for the well. These together lead to a horrible issue where 22M could have been stolen. Overall, a fairly simple attack. The zero length array trick was fascinating to me on this though. This bypassed some math been done and sanity checks, which was super cool to see.
Analysis Summary
# Vulnerability: Logic Flaws in Beanstalk `convertFacet` Leading to Protocol Drain
## CVE Details
- **CVE ID**: Not Assigned (DeFi Protocol Incident)
- **CVSS Score**: 9.9 (Critical) - *Estimated based on potential impact.*
- **CWE**: CWE-20 (Improper Input Validation), CWE-670 (Always-Incorrect Control Flow Implementation)
## Affected Systems
- **Products**: Beanstalk Stablecoin Protocol
- **Versions**: Deployments active leading up to November 8, 2023.
- **Configurations**: Smart contracts containing `convertFacet` and `LibWellConvert` logic.
## Vulnerability Description
The vulnerability stems from two primary logic failures within the `convert()` function and the `convertFacet` contract:
1. **Missing Array Length Validation**: The function accepts arrays (`stems` and `amounts`) without verifying they contain data. By passing zero-length arrays, an attacker can bypass certain mathematical sanity checks and state updates that would normally occur during a legitimate conversion.
2. **Unvalidated Well Addresses**: When the conversion type is set to `WELL_LP_TO_BEANS`, the protocol requires a "Well" (liquidity pool) address. The code failed to verify if the provided address was on the protocol’s allowlist. This allowed an attacker to inject a malicious/spoofed contract address.
3. **The Attack Path**: By combining these flaws, an attacker could point the protocol to a fake Well. Because of the zero-length array bypass, the protocol would execute `_wellRemoveLiquidityTowardsPeg` against the fake Well. The fake Well returns a negligible value, but the protocol incorrectly registers a BEAN deposit to the attacker's account without the attacker providing real underlying collateral.
## Exploitation
- **Status**: Confirmed/PoC Available (Reported via Bug Bounty; not exploited in the wild prior to disclosure).
- **Complexity**: Medium
- **Attack Vector**: Network (Smart Contract Interaction)
## Impact
- **Confidentiality**: None
- **Integrity**: High (Unauthorized creation of Bean deposits)
- **Availability**: High (Total drain of protocol liquidity - ~22.8M Beans at risk)
## Remediation
### Patches
- The Beanstalk protocol addressed the issue in response to Report #25519. Updates were pushed to the `convertFacet` and `LibWellConvert` libraries to ensure all Well addresses are validated against the protocol's whitelist and that input arrays are non-zero.
### Workarounds
- There were no viable user-side workarounds; the fix required a protocol-level upgrade via the DAO/governance process (Emergency BIP).
## Detection
- **Indicators of Compromise**: Transactions calling `convert()` with empty `stems[]` or `amounts[]` arrays, or `convertData` containing contract addresses not previously seen in the Beanstalk ecosystem.
- **Detection methods and tools**: Monitoring of event logs for `Convert` events that lack corresponding token transfers or involve unverified pool addresses.
## References
- **Vendor Advisory**: [BIR-7: Verify Whitelisted Pool for Converts](https://arweave[.]net/qWsmsa0ErqWj-ul9PcDuE0EfLh9eiwhtdhWpCzBMmQY)
- **Immunefi/Bug Report**: Report #25519