The most direct impact is the theft of collected bridge fees. If a sweeper is no longer supposed to have access but can still initiate a sweep due to front-running the permission revocation, they can transfer potentially significant amounts of bridge fees to themselves. This results in a direct financial loss to the protocol and its users.
function sweep() public payable nonReentrant {
// Verify the caller is whitelisted
if (!allowedBridgeSweepers[msg.sender]) {
revert UnauthorizedBridgeSweeper();
}
// Get the balance of nextWETH in the contract
uint256 balance = collateralToken.balanceOf(address(this));
// If there is no balance, return
if (balance == 0) {
revert InvalidZeroOutput();
}
// Approve it to the connext contract
collateralToken.safeApprove(address(connext), balance);
// Need to send some calldata so it triggers xReceive on the target
bytes memory bridgeCallData = abi.encode(balance);
connext.xcall{ value: msg.value }(
bridgeDestinationDomain,
bridgeTargetAddress,
address(collateralToken),
msg.sender,
balance,
0, // Asset is already nextWETH, so no slippage will be incurred
bridgeCallData
);
// send collected bridge fee to sweeper
_recoverBridgeFee();
// Emit the event
emit BridgeSwept(bridgeDestinationDomain, bridgeTargetAddress, msg.sender, balance);
}
Assume that the smart contract is deployed, and various bridge sweeper addresses (sweeperA, sweeperB, etc.) are authorized to initiate the sweep() function, which moves tokens and collecting bridge fees.
setAllowedBridgeSweeper(address _sweeper, bool _allowed) is used to manage permissions for these sweeper addresses.
Step 2: Change in Permissions
Suppose the owner decides to revoke the permission of sweeperA due to a policy change, suspicious activity, or other reasons.
The owner calls setAllowedBridgeSweeper(sweeperA, false) to update the permission status of sweeperA.
Step 3: Front-Running Opportunity
Observation by Sweeper: sweeperA is monitoring pending transactions on the blockchain (which is common practice for bots or individuals looking to exploit front-running opportunities).
Front-Running Transaction: Seeing the pending transaction that would revoke their permissions, sweeperA quickly initiates a sweep() transaction with a higher gas price to ensure it gets mined before the permission revocation.
Step 4: Execution of sweep() Function
Check of Permissions: When the sweep() function is called, it first checks if the caller is an allowed bridge sweeper: if (!allowedBridgeSweepers[msg.sender]) { revert UnauthorizedBridgeSweeper(); }. Since the revocation transaction is still pending, sweeperA is still authorized.
Transfer of Funds: The sweep() function proceeds to transfer the collected bridge fees or other assets as it normally would, sending these to sweeperA.
Step 5: Confirmation of Transactions
Confirmation of sweep(): The front-running sweep() transaction by sweeperA is confirmed first due to the higher gas price, successfully transferring bridge fees sweeperA.
Confirmation of Permission Change: The setAllowedBridgeSweeper(sweeperA, false) transaction is confirmed after the sweep(). By this time, the misappropriation has already occurred.
Tools Used
Manual Audit
Recommended Mitigation Steps
Introduce a timelock mechanism that delays the enforcement of permission changes. For instance, when setAllowedBridgeSweeper is called, the change could be stored in a pending state and only applied after a certain cooling-off period (e.g., 24 hours).
Lines of code
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Bridge/L2/xRenzoDeposit.sol#L466-L470 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Bridge/L2/xRenzoDeposit.sol#L414-L449 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Bridge/L2/xRenzoDeposit.sol#L396-L406
Vulnerability details
Impact
The most direct impact is the theft of collected bridge fees. If a sweeper is no longer supposed to have access but can still initiate a sweep due to front-running the permission revocation, they can transfer potentially significant amounts of bridge fees to themselves. This results in a direct financial loss to the protocol and its users.
Proof of Concept
POC
Step 1: Initial Setup
Assume that the smart contract is deployed, and various bridge sweeper addresses (sweeperA, sweeperB, etc.) are authorized to initiate the sweep() function, which moves tokens and collecting bridge fees.
setAllowedBridgeSweeper(address _sweeper, bool _allowed) is used to manage permissions for these sweeper addresses.
Step 2: Change in Permissions
Suppose the owner decides to revoke the permission of sweeperA due to a policy change, suspicious activity, or other reasons.
The owner calls setAllowedBridgeSweeper(sweeperA, false) to update the permission status of sweeperA.
Step 3: Front-Running Opportunity
Observation by Sweeper: sweeperA is monitoring pending transactions on the blockchain (which is common practice for bots or individuals looking to exploit front-running opportunities).
Front-Running Transaction: Seeing the pending transaction that would revoke their permissions, sweeperA quickly initiates a sweep() transaction with a higher gas price to ensure it gets mined before the permission revocation.
Step 4: Execution of sweep() Function
Check of Permissions: When the sweep() function is called, it first checks if the caller is an allowed bridge sweeper: if (!allowedBridgeSweepers[msg.sender]) { revert UnauthorizedBridgeSweeper(); }. Since the revocation transaction is still pending, sweeperA is still authorized.
Transfer of Funds: The sweep() function proceeds to transfer the collected bridge fees or other assets as it normally would, sending these to sweeperA.
Step 5: Confirmation of Transactions
Confirmation of sweep(): The front-running sweep() transaction by sweeperA is confirmed first due to the higher gas price, successfully transferring bridge fees sweeperA.
Confirmation of Permission Change: The setAllowedBridgeSweeper(sweeperA, false) transaction is confirmed after the sweep(). By this time, the misappropriation has already occurred.
Tools Used
Manual Audit
Recommended Mitigation Steps
Introduce a timelock mechanism that delays the enforcement of permission changes. For instance, when
setAllowedBridgeSweeper
is called, the change could be stored in a pending state and only applied after a certain cooling-off period (e.g., 24 hours).Assessed type
MEV