Short-Calldata Zero-Padding Vulnerability in Diamond Proxy Allows Unexpected Fallback Execution
Report ID
#38420
Report type
Smart Contract
Has PoC?
Yes
Target
https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5
Impacts
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
A subtle edge case exists in Beanstalk's Diamond proxy (an EIP-2535 implementation) where any msg.data.length < 4 call leads to zero-padding of msg.sig. For example, calldata of just 0xff becomes 0xff000000. If a facet with that partial selector (0xff000000) is added (e.g., via governance), and its fallback function contains harmful logic, it could be unintentionally or maliciously triggered—potentially resulting in unexpected state changes.
Vulnerability Details
Within the Diamond proxy's fallback, we observe:
The issue arises from Solidity's handling of msg.sig (a bytes4) when msg.data.length < 4. Zero-padding can map less-than-four-bytes calldata—like 0xff—to a 4-byte selector 0xff000000. If a facet with that exact selector has a fallback function, the Diamond proxy will delegate-call to it:
contract TestFacet {
// The function selector is 0xff000000
function BlazingIt6886408584() public payable {}
fallback() external {
// Potentially malicious or unexpected logic
}
}
...
// Sending only one byte ("0xff") triggers the delegatecall to TestFacet
// and fallback would be executed
address(beanstalk).call(hex"ff");Potential for "Selector Mining"
Attackers can also perform selector mining to generate specific 4-byte function selectors that end (or begin) with particular bytes. By brute-forcing various function signatures (e.g., tweaking the name or parameters), they may deliberately produce a function that looks legitimate but with a selector like 0xff000000 (or another that partially matches short calldata). Once introduced via governance or other means, this seemingly innocuous facet function could allow fallback-based exploits to remain hidden in plain sight.
Why Might Calldata < 4 Occur?
While it's true that calls with fewer than 4 bytes are not typical, in my opinion they are still possible in several scenarios:
- Phishing or Social Engineering: A malicious interface could trick users into sending a transaction. The user might think it's a harmless or empty call, when in fact it activates an unexpected fallback.
- Malicious Contract-to-Contract Calls: Attackers could craft a contract that deliberately sends very short calldata to exploit the zero-padded selector, making it difficult for users of the contract to detect the issue.
- Accidental or Testing Mistakes: Under certain circumstances, both humans and programs can make mistakes, which may lead to unintended outcomes due to the unexpected execution path.
In isolation, this might appear edge-case, but if a malicious facet is successfully introduced—especially via governance—this quirk becomes a potential vector for unauthorized actions.
Impact Details
- Unexpected Execution Path: By exploiting zero-padding, a single-byte (or otherwise short) calldata can invoke a fallback that was never meant to be called.
- Potentially Harmful Operations: If the fallback function executes transfers, mints, or any privileged logic, it could pose a real threat to protocol security and user funds.
This vulnerability doesn't immediately imply direct theft under normal circumstances. However, it still presents a risk because malicious fallback logic could be exploited for griefing, unauthorized state changes, or, depending on the facet's code, even fund misappropriation.
Proof of concept
BIC Response
The situation you describe requires (1) a malicious fallback function to be introduced to the contracts and (2) an end user to unwittingly interact with said malicious function. These observations are not relevant to our bug bounty program and thus we are closing the report.