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

Critical Vulnerability in BeanL2MigrationFacet Potential Loss of Tokens During L2 Bean Migration.

Summary

file: https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/beanstalk/migration/BeanL2MigrationFacet.sol

In the event of an incorrect L2Beanstalk address being used, tokens will be burned and permanently lost. To prevent this critical issue, the L2Beanstalk address must be thoroughly checked and validated.

function migrateL2Beans(
address reciever,
@>>> address L2Beanstalk,
uint256 amount,
uint32 gasLimit
) external nonReentrant {
C.bean().burnFrom(msg.sender, amount);
// send data to
IL2Bridge(BRIDGE).sendMessage(
L2Beanstalk,
abi.encodeCall(IBeanL1RecieverFacet(L2Beanstalk).recieveL1Beans, (reciever, amount)),
gasLimit
);
}

Vulnerability Details

The BeanL2MigrationFacet::migrateL2Beans function lacks a check for the L2Beanstalk address when a user attempts to migrate Bean tokens to L2.
When the function is called, the user passes the L2Beanstalk address which will mint beans on L2.
Regardless of the address passed, the message is sent to IL2Bridge(BRIDGE).sendMessage Upon relayMessage calling the target address, the SafeCall.call on relayMessage will not return false because if contract doesn't exists the target address will be treated as EOA and the EOA do not revert if a function triggered on an EOA address. Therefore, the user on L1 who sent tokens will not be able to replay the message on L2 and beans tokens are lost for ever.

POC: it will not revert.

function relayMessage(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _minGasLimit,
bytes calldata _message
) external payable {
// ...snip
xDomainMsgSender = _sender;
@>> // @notice it will return true every time if _target is EOA.
bool success = SafeCall.call(_target, gasleft() - RELAY_RESERVED_GAS, _value, _message);
xDomainMsgSender = Constants.DEFAULT_L2_SENDER;
// ...snip
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Test {
function relayMessage() external {
address random_EOA = address(0x1122334455);
(bool s, ) = random_EOA.call(abi.encodeWithSignature("randomFunction()"));
require(s, "Failed!!!");
}
}

lets take a look:

1- alice wants to migrate 100,000 bean token to L2.
2- she calls BeanL2MigrationFacet::migrateL2Beans and she passed amount: 100,000
3- L2Beanstalk address is: 0x12345... but she passes 0x12346...
4- message sent to L2 via L1CrossDomainMessenger.
5- on L2 relayMessage is triggered and send transaction using SafeCall.call to call 0x12346... but since this address is not the real address 0x12345... it will execute and return true because 0x12346... is treated as EOA.
6- at this point alice lost her 100,000 bean tokens for ever.

Impact

The Vulnerability results in the permanent loss of Bean tokens for users who accidentally pass an incorrect L2Beanstalk address during the migration process. This not only affects users but also the overall integrity of the migration mechanism.

Tools Used

Recommendations

Implement validation checks to ensure that the L2Beanstalk address is correct before proceeding with the migration.

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 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.