DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: low
Invalid

ReseedBean lacks the inclusion of msg.sender in the SALT values, which leaves it open to re-org attacks

Summary

The contract deployment method in the ReseedBean contract does not consider potential re-org attacks.

It uses the create2 opcode with fixed salt values, which does not include the msg.sender.

This approach makes the contract deployments vulnerable to re-org attacks, especially on EVM-compatible L2 solutions like Arbitrum and Polygon, which are more susceptible to re-org events.

Vulnerability Details

In the current implementation, the ReseedBean contract deploys other contracts using the ERC1967Proxy
with a fixed salt. For example:

address beanEthWell = address(
new ERC1967Proxy{salt: BEAN_ETH_SALT}(
CP2_U_BEAN_ETH_WELL_IMPLMENTATION,
abi.encodeCall(IWellUpgradeable.init, (BEAN_ETH_NAME, BEAN_ETH_SYMBOL, OWNER))
)
);

This method does not incorporate msg.sender into the salt, making the derived contract address solely
based on the factory's nonce and the fixed salt. Consequently, it does not mitigate the risk of re-org
attacks effectively.

This is also existential in deployBean, deployUnripeBean, deployUnripeLP, deployBeanEthWell.

Impact

Contracts deployed using the current method are vulnerable to re-org attacks. Re-orgs, or chain
reorganizations, can occur across all EVM chains but are more frequent on EVM-compatible L2 solutions
such as Arbitrum and Polygon. Ethereum, a primary deployment target, has already experienced re-org events.

During re-org events, transactions that were once included in a block may be reverted.
When re-executed, the nonce of the factory contract could have changed, causing the contract address derived
from create2 to differ from the expected address.

EVM-compatible L2 solutions are more susceptible to re-org events due to their inherent design.
Deploying contracts on such chains without addressing the re-org vulnerability can lead to inconsistent
deployment patterns and unexpected contract addresses.

Re-org attacks can be exploited by malicious actors to disrupt the deployment process, potentially leading to denial of service
or other security issues.

Tools Used

Manual Review

Recommendations

To mitigate the risk of re-org attacks and enhance the security of contract deployments,
it is recommended to use create2 with a dynamically generated salt that includes msg.sender.
This approach ensures that the derived contract address is more predictable and less prone to
unexpected changes due to re-orgs.

The deployment functions in the ReseedBean contract should be modified to include msg.sender in the salt, as shown below:

function deployBean(uint256 supply) internal returns (BeanstalkERC20) {
bytes32 salt = keccak256(abi.encodePacked(BEAN_SALT, msg.sender));
BeanstalkERC20 bean = new BeanstalkERC20{salt: salt}(
address(this),
BEAN_NAME,
BEAN_SYMBOL
);
bean.mint(address(this), supply);
return bean;
}
function deployUnripeBean(uint256 supply) internal returns (BeanstalkERC20) {
bytes32 salt = keccak256(abi.encodePacked(UNRIPE_BEAN_SALT, msg.sender));
BeanstalkERC20 unripeBean = new BeanstalkERC20{salt: salt}(
address(this),
UNRIPE_BEAN_NAME,
UNRIPE_BEAN_SYMBOL
);
unripeBean.mint(address(this), supply);
return unripeBean;
}
function deployUnripeLP(uint256 supply) internal returns (BeanstalkERC20) {
bytes32 salt = keccak256(abi.encodePacked(UNRIPE_LP_SALT, msg.sender));
BeanstalkERC20 unripeLP = new BeanstalkERC20{salt: salt}(
address(this),
UNRIPE_LP_NAME,
UNRIPE_LP_SYMBOL
);
unripeLP.mint(address(this), supply);
return unripeLP;
}
function deployBeanEthWell(
BeanstalkERC20 bean,
WellAmountData calldata beanEth
) internal returns (IWell) {
bytes32 salt = keccak256(abi.encodePacked(BEAN_ETH_SALT, msg.sender));
address beanEthWell = address(
new ERC1967Proxy{salt: salt}(
CP2_U_BEAN_ETH_WELL_IMPLMENTATION,
abi.encodeCall(IWellUpgradeable.init, (BEAN_ETH_NAME, BEAN_ETH_SYMBOL, OWNER))
)
);
return mintAndSync(bean, beanEthWell, beanEth.beansInWell, beanEth.nonBeanTokensInWell);
}
function deployBeanWstEthWell(
BeanstalkERC20 bean,
WellAmountData calldata beanWsteth
) internal returns (IWell) {
bytes32 salt = keccak256(abi.encodePacked(BEAN_WSTETH_SALT, msg.sender));
address beanWstEthWell = address(
new ERC1967Proxy{salt: salt}(
CP2_U_BEAN_WSTETH_WELL_IMPLMENTATION,
abi.encodeCall(IWellUpgradeable.init, (BEAN_WSTETH_NAME, BEAN_WSTETH_SYMBOL, OWNER))
)
);
return mintAndSync(bean, beanWstEthWell, beanWsteth.beansInWell, beanWsteth.nonBeanTokensInWell);
}
function deployBeanStableWell(
BeanstalkERC20 bean,
WellAmountData calldata beanStable
) internal returns (IWell) {
bytes32 salt = keccak256(abi.encodePacked(BEAN_STABLE_SALT, msg.sender));
address beanStableWell = address(
new ERC1967Proxy{salt: salt}(
SS_U_BEAN_STABLE_WELL_IMPLMENTATION,
abi.encodeCall(IWellUpgradeable.init, (BEAN_STABLE_NAME, BEAN_STABLE_SYMBOL, OWNER))
)
);
return mintAndSync(bean, beanStableWell, beanStable.beansInWell, beanStable.nonBeanTokensInWell);
}

By incorporating msg.sender in the salt, the contract addresses are no longer solely dependent on the factory's nonce and a fixed salt, thereby enhancing security against re-org threats and ensuring more consistent deployment patterns in line with CREATE2.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational/Gas

Invalid as per docs https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.