Core root router will always revert when user put _params parameter while bridging.
Proof of Concept
In CoreRootRouter contract, executeDepositSingle, executeDepositMultiple, executeSigned, executeSignedDepositSingle, executeSignedDepositMultiple functions will always be reverted.
These functions are called when bridging with _params parameter at BranchBridgeAgent contract. This is BranchBridgeAgent contract’s functions which get _params parameter.
_params parameter is for custom root router. The custom router get information about what to do from _params parameter. But core root router doesn’t provide custom action for user, so if _params parameter exist, it just revert the transaction.
This is RootBridgeAgentExecutor contract functions which call router functions. They call router function only when _params parameter exist. And this would be reverted if router is core root router.
function executeWithDeposit(address _router, bytes calldata _payload, uint16 _srcChainId)
external
payable
onlyOwner
{
...
// Check if there is additional calldata in the payload
if (_payload.length > PARAMS_TKN_SET_SIZE) {
//Execute remote request
IRouter(_router).executeDepositSingle{value: msg.value}(
_payload[PARAMS_TKN_SET_SIZE:], dParams, _srcChainId
);
}
}
function executeWithDepositMultiple(address _router, bytes calldata _payload, uint16 _srcChainId)
external
payable
onlyOwner
{
...
// Check if there is additional calldata in the payload
if (length > PARAMS_END_OFFSET + (numOfAssets * PARAMS_TKN_SET_SIZE_MULTIPLE)) {
//Try to execute remote request
IRouter(_router).executeDepositMultiple{value: msg.value}(
_payload[PARAMS_END_OFFSET + uint256(numOfAssets) * PARAMS_TKN_SET_SIZE_MULTIPLE:], dParams, _srcChainId
);
}
}
function executeSignedNoDeposit(address _account, address _router, bytes calldata _payload, uint16 _srcChainId)
external
payable
onlyOwner
{
//Execute remote request
IRouter(_router).executeSigned{value: msg.value}(_payload[PARAMS_TKN_START_SIGNED:], _account, _srcChainId);
}
function executeSignedWithDeposit(address _account, address _router, bytes calldata _payload, uint16 _srcChainId)
external
payable
onlyOwner
{
...
// Check if there is additional calldata in the payload
if (_payload.length > PARAMS_SETTLEMENT_OFFSET) {
//Execute remote request
IRouter(_router).executeSignedDepositSingle{value: msg.value}(
_payload[PARAMS_SETTLEMENT_OFFSET:], dParams, _account, _srcChainId
);
}
}
function executeSignedWithDepositMultiple(
address _account,
address _router,
bytes calldata _payload,
uint16 _srcChainId
) external payable onlyOwner {
...
// Check if there is additional calldata in the payload
if (
_payload.length
> PARAMS_END_SIGNED_OFFSET
+ uint256(uint8(bytes1(_payload[PARAMS_START_SIGNED]))) * PARAMS_TKN_SET_SIZE_MULTIPLE
) {
//Execute remote request
IRouter(_router).executeSignedDepositMultiple{value: msg.value}(
_payload[
PARAMS_END_SIGNED_OFFSET
+ uint256(uint8(bytes1(_payload[PARAMS_START_SIGNED]))) * PARAMS_TKN_SET_SIZE_MULTIPLE:
],
dParams,
_account,
_srcChainId
);
}
}
If the user did not use the fallback option, they cannot call redeemDeposit immediately, but must call retrieveDeposit first to request token back.
The automatic fallback option is not provided for the callOutAndBridge, callOutAndBridgeMultiple, and callOutSigned functions, so the user must call retrieveDeposit and redeemDeposit to get their tokens back. Or the user should modify the request with retryDeposit to delete the _params parameter and resend it.
Users who have spent all their tokens bridging will have a hard time getting their tokens out because they can't pay for gas anymore.
To avoid this situation, the core branch bridge agent or core branch router should limit the length of the _params to zero.
Tools Used
Manual Review
Recommended Mitigation Steps
You can restrict _params on the core branch router, but it is not possible to prevent all of these situations because user can call the functions of the Branch bridge agent without going through the router.
One suggestion is to have the isCore setting on the Branch bridge agent or branch router, and if it is true, limit the length of the _params .
If you want to use Core contracts solely for administrative purposes, it would be better to prevent users from requesting token moves to core.
Lines of code
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L350-L357 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L360-L367 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L370-L372 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L375-L382 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L385-L392
Vulnerability details
Impact
Core root router will always revert when user put
_params
parameter while bridging.Proof of Concept
In CoreRootRouter contract,
executeDepositSingle
,executeDepositMultiple
,executeSigned
,executeSignedDepositSingle
,executeSignedDepositMultiple
functions will always be reverted.These functions are called when bridging with
_params
parameter at BranchBridgeAgent contract. This is BranchBridgeAgent contract’s functions which get_params
parameter.https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L209-L336
_params
parameter is for custom root router. The custom router get information about what to do from_params
parameter. But core root router doesn’t provide custom action for user, so if_params
parameter exist, it just revert the transaction.This is RootBridgeAgentExecutor contract functions which call router functions. They call router function only when
_params
parameter exist. And this would be reverted if router is core root router.https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L82-L235
If the user did not use the fallback option, they cannot call
redeemDeposit
immediately, but must callretrieveDeposit
first to request token back.The automatic fallback option is not provided for the
callOutAndBridge
,callOutAndBridgeMultiple
, andcallOutSigned
functions, so the user must callretrieveDeposit
andredeemDeposit
to get their tokens back. Or the user should modify the request withretryDeposit
to delete the_params
parameter and resend it.Users who have spent all their tokens bridging will have a hard time getting their tokens out because they can't pay for gas anymore.
To avoid this situation, the core branch bridge agent or core branch router should limit the length of the
_params
to zero.Tools Used
Manual Review
Recommended Mitigation Steps
You can restrict
_params
on the core branch router, but it is not possible to prevent all of these situations because user can call the functions of the Branch bridge agent without going through the router.One suggestion is to have the
isCore
setting on the Branch bridge agent or branch router, and if it is true, limit the length of the_params
.If you want to use Core contracts solely for administrative purposes, it would be better to prevent users from requesting token moves to core.
Assessed type
DoS