DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: high
Valid

Permanent Loss in Bean Token L1 to L2 Migration Process.

Summary

This report highlights a critical vulnerability in the Bean token migration process from L1 to L2. The migration process involves several steps, including burning tokens on L1, sending messages between L1 and L2, and minting tokens on L2. However, a logical condition in the migration contract on L2 can result in the permanent loss of user tokens, making the migration process unsafe and unreliable.

Vulnerability Details

When a user initiates the migration of their Bean tokens to L2, the following steps are executed:

1- Burning Tokens on L1: The user calls BeanL2MigrationFacet::migrateL2Beans, which burns the tokens and sends a message to the L1CrossDomainMessenger.
2- Message Transmission to L2: The L1CrossDomainMessenger::sendMessage passes the message to OptimismPortal::depositTransaction.
3- Minting Tokens on L2: On L2, the relayMessage function sends a transaction to trigger BeanL1ReceiverFacet::receiveL1Beans to mint the Bean tokens.

The critical issue arises in the BeanL1ReceiverFacet.sol contract, specifically within the following condition:

link: https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/beanstalk/migration/BeanL1RecieverFacet.sol#L39-L42

require(
EXTERNAL_L1_BEANS >= s.sys.migration.migratedL1Beans,
"L2Migration: exceeds maximum migrated"
);

Lets take simple example:

1- EXTERNAL_L1_BEANS constent is set to 10,000,000.
2- users migrated 9,000,000 beans tokens onto L2.
3- alice wants to migrate 1,000,010 tokens to L2.
4- alice calls migrateL2Beans and burn 1,000,010 beans.
5- a message sent to sendMessage and relayMessage on L2 is triggered.
6- now when it comes to check:

@>> // 9,000,000 += 1,000,010 -> 10,000,010
s.sys.migration.migratedL1Beans += amount;
require(
@>> // 10,000,000 >= 10,000,010 -> this will be false so it will revert.
EXTERNAL_L1_BEANS >= s.sys.migration.migratedL1Beans,
"L2Migration: exceeds maximum migrated"
);

7- now the message cannot be execute and 1,000,010 beans on L1 are lost for ever.

If the condition EXTERNAL_L1_BEANS >= s.sys.migration.migratedL1Beans is false, the transaction will revert.
This condition prevents the execution of relayMessage and consequently, the minting of Bean tokens on L2.
Since the Bean tokens on L1 are already burned, users cannot restore their tokens on L1. Furthermore, even though the relayMessage function allows
to replay message if it is fail, it will continue to revert, leading to the permanent loss of tokens.

Impact

it results in the irreversible loss of Bean tokens for users attempting to migrate their tokens from L1 to L2.
This affects the overall security and reliability of the Bean token migration process.

Tools Used

Recommendations

add a check on L1, and keep tracking how many bean tokens are migrated onto L2 by adding a counter.

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

function migrateL2Beans(
address reciever,
address L2Beanstalk,
uint256 amount,
uint32 gasLimit
) external nonReentrant {
--- // NOTE: consider Diamond storage layout order.
+ s.sys.migration.migratedL1Beans += amount;
+ require(
+ EXTERNAL_L1_BEANS >= s.sys.migration.migratedL1Beans,
+ "L2Migration: exceeds maximum migrated"
+ );
C.bean().burnFrom(msg.sender, amount);
// send data to
IL2Bridge(BRIDGE).sendMessage(
L2Beanstalk,
abi.encodeCall(IBeanL1RecieverFacet(L2Beanstalk).recieveL1Beans, (reciever, amount)),
gasLimit
);
}
Updates

Lead Judging Commences

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

If EXTERNAL_L1_BEANS check performed on L2 fails then the burned beans are lost for ever

Appeal created

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

If EXTERNAL_L1_BEANS check performed on L2 fails then the burned beans are lost for ever

Support

FAQs

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