xanoutas/solidity-security-findings huggingface dataset:
A malicious user can spam ghost deposits to DoS the LayerZeroEndpoint messaging layer
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L765-L778


# Vulnerability details

## Impact

There can be many deposits that will strain the messaging layer, especially in chains that do not cost much to send a transaction.

## Proof of Concept

The entry point of the bridge is callOutSignedAndBridge in BranchBridgeAgent.sol. This function will pack data for the cross-chain call, call [`_createDeposit`](https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L299) and call [`_performCall`](https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L302).

Looking at _performCall first, it simply sends a message to the layer zero endpoint.

```
        ILayerZeroEndpoint(lzEndpointAddress).send{value: msg.value}(
            rootChainId,
            rootBridgeAgentPath,
            _payload,
            payable(_refundee),
            address(0),
            abi.encodePacked(uint16(2), _gParams.gasLimit, _gParams.remoteBranchExecutionGas, rootBridgeAgentAddress)
        );
```

Now look at _createDeposit. _createDeposit calls bridgeOut from the Port contract and updates the deposit storage variable. _bridgeOut in the port contract checks how much hTokens and underlying tokens are being bridged out.

```
        // Check if hTokens are being bridged out
        if (_hTokenAmount > 0) {
            _localAddress.safeTransferFrom(_depositor, address(this), _hTokenAmount);
            ERC20hTokenBranch(_localAddress).burn(_hTokenAmount);
        }
        // Check if underlying tokens are being bridged out
        if (_deposit > 0) {
            _underlyingAddress.safeTransferFrom(_depositor, address(this), _deposit);
```

Interestingly, the function does not revert if hTokenAmount and _deposit is 0. This means that a malicious user can spam call `callOutSignedAndBridge` or any other variants like `callOutSignedAndBridgeMultiple` and send in a ghost deposit, which sends nothing of value to the layer, and simply attempt to DoS the messaging layer.

## Tools Used

VSCode

## Recommended Mitigation Steps

Make sure that the function either reverts when nothing of value is sent, or that the protocol takes a small sum whenever the bridge is called so as to deter these DoS calls.


## Assessed type

DoS
Gas that was sent by LayerZero can get stuck in the contract in some cases
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L749
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L712


# Vulnerability details

If a tx on the destination chain calls back the chain from where the transaction was initiated by the user, the first transaction on the source chain needs to ["airdrop"](https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters#airdrop) gas to the destination chain so it is able to call back the source chain. 

The problem is that when the transaction on the destination chain fails and there is no fallback, gas that was sent from the source chain isnt refunded and it will be stuck on the destinations chain bridge agent. 

Not every operation type allows you to use the fallback like system requests or operations that are not signed so if the transaction fails then the airdropped gas will be stuck and it will be used by other users. 

Example:

1. Two users want to add the same global token and they both call `addGlobalToken()` on the branch chain. When `addGlobalToken()` is used then you need to airdrop the gas for 2 transactions
2. The transaction that was first caught by LayerZero will be executed succesfuly on the destination chain
3. However the second transaction will fail because the token is already added and the `_execute()` without fallback was used
4. The gas that was sent to the root chain will be locked in the `RootBridgeAgent` and it can then be used by other users

Please not that this doesnt only have to be a system request it can also be a non-signed operation or when fallback isnt toggled. 

## Impact

Ether will be stuck in the `BridgeAgent` contract and the user that called the transaction wont be able to get it back and it will then be taken by other users when they for example execute `_performFallbackCall()` where the LayerZero will refund them the contract balance. 


## Proof of Concept

Add this to `RootForkTest.t.sol`, the poc shows that if the second transaction fails then funds will be stuck in the `RootBridgeAgent`. 

```solidity
function testAddGlobalTokenFails() public {

        //Add Local Token from Avax
        testAddLocalToken();

        console2.log("BALANCE OF ROOT BRIDGE AGENT BEFORE", address(coreRootBridgeAgent).balance);

        //the tx fails on the second iteration, because token already added
        for(uint256 i; i < 2; i++) {
            switchToLzChain(avaxChainId);

            vm.deal(address(this), 1000 ether);

            GasParams[3] memory gasParams =
                [GasParams(15_000_000, 0.1 ether), GasParams(2_000_000, 3 ether), GasParams(200_000, 0)];

            avaxCoreRouter.addGlobalToken{value: 1000 ether}(newAvaxAssetGlobalAddress, ftmChainId, gasParams);

            //Switch Chain and Execute Incoming Packets
            switchToLzChain(rootChainId);
            //Switch Chain
Anyone can create multiple RootBridgeAgent
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/factories/RootBridgeAgentFactory.sol#L48-L59


# Vulnerability details

## Impact
createBridgeAgent is used to create a new Root Bridge Agent. This new RootBridgeAgent is added to the array on the rootPort. 
This can be used to randomly spam the array with thousand of array addresses whereby the ones deployed by the protocol are harder to find
## Proof of Concept
```solidity
    function createBridgeAgent(address _newRootRouterAddress) external returns (address newBridgeAgent) {
        newBridgeAgent = address(
            new RootBridgeAgent(
                rootChainId, 
                lzEndpointAddress, 
                rootPortAddress, 
                _newRootRouterAddress
            )
        );


        IRootPort(rootPortAddress).addBridgeAgent(msg.sender, newBridgeAgent);
    }
```

## Tools Used
Manual Review 
## Recommended Mitigation Steps
Set the access control on ``createBridgeAgent``


## Assessed type

Access Control
Many issues around `addGlobalToken` due to lack of input validation when linking a global token to local token
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/RootPort.sol#L264-L268


# Vulnerability details



## Impact
`addGlobalToken` is used to add a global token to a branch. 
The flow as follows:

1 => `CoreBranchRouter.addGlobalToken`

2 => Send Cross-Chain request (System Response/Request) with FuncId  0x01 (Notice that is uses normal callOut though. Anyway, let's continue)
[CoreBranchRouter: L51-L55](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreBranchRouter.sol#L51-L55)

3 => Root receives the request to `lzReceiveNonBlocking` and calls `RootBridgeAgentExecutor.executeNoDeposit`
[RootBridgeAgent: L482-L489](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/RootBridgeAgent.sol#L482-L489)

4 => RootBridgeAgentExecutor calls `CoreRootRouter.execute` 
[CoreRootRouter: L336-L343](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreRootRouter.sol#L336-L343)

5 => **CoreRootRouter** calls `_addGlobalToken` internally where it **only checks if the GlobalToken is unrecognized and the token was not already added**
```solidity
  if (!IPort(rootPortAddress).isGlobalAddress(_globalAddress)) {
            revert UnrecognizedGlobalToken();
        }

        // Verify that it does not exist
        if (IPort(rootPortAddress).isGlobalToken(_globalAddress, _dstChainId)) {
            revert TokenAlreadyAdded();
        }
```
[CoreRootRouter: L414-L422](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreRootRouter.sol#L414-L422)

6 => Send Cross-Chain request (System Response/Request) with FuncId  0x01 to the branch chain
[CoreRootRouter: L434-L440](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreRootRouter.sol#L434-L440)

7 => Branch receives the request to `lzReceiveNonBlocking` and calls `BranchBridgeAgentExecutor.executeWithSettlement`

8 => BranchBridgeAgentExecutor calls `CoreBranchRouter.executeNoSettlement` 
[CoreBranchRouter: L53-L57](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/BranchBridgeAgentExecutor.sol#L53-L57)

9 => CoreBranchRouter calls `_receiveAddGlobalToken` internally
[CoreBranchRouter: L88-L99](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreBranchRouter.sol#L88-L99)

10 => `_receiveAddGlobalToken` calls `IBridgeAgent.callOutSystem` with FuncId (0x03)
[CoreBranchRouter: L181-L185](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/CoreBranchRouter.sol#L181-L185)

11 => `IBridgeAgent.callOutSystem` send Cross-Chain request with FuncId  0x00 
[BranchBridgeAgent: L188-L192](https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/BranchBridgeAgent.sol#L188-L192)

12 => Root receives
[M-14] Reentrancy in the RootBridgeAgent contract
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L140-L153
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L233-L271
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L274-L296
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L299-L344
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L351-L384
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L423-L431
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L434-L737


# Vulnerability details

## Impact
In a Re-entrancy attack, a malicious contract calls back into the calling contract before the first invocation of the function is finished. 
This may cause the different invocations of the function to interact in undesirable ways, especially in cases where the function is updating state variables after the external calls.
This may lead to loss of funds, improper value updates, token loss, etc.
## Proof of Concept

**Vulnerable getFeeEstimate function**
```sol
// Line: 140-153
    function getFeeEstimate(
        uint256 _gasLimit,
        uint256 _remoteBranchExecutionGas,
        bytes calldata _payload,
        uint16 _dstChainId
    ) external view returns (uint256 _fee) {
        (_fee,) = ILayerZeroEndpoint(lzEndpointAddress).estimateFees(
            _dstChainId,
            address(this),
            _payload,
            false,
            abi.encodePacked(uint16(2), _gasLimit, _remoteBranchExecutionGas, getBranchBridgeAgent[_dstChainId])
        );
    }
```
**Vulnerable retrySettlement function**
```sol
// Line 233-271
    function retrySettlement(
        uint32 _settlementNonce,
        address _recipient,
        bytes calldata _params,
        GasParams calldata _gParams,
        bool _hasFallbackToggled
    ) external payable override lock {
        // Get storage reference
        Settlement storage settlementReference = getSettlement[_settlementNonce];


        // Check if Settlement hasn't been redeemed.
        if (settlementReference.owner == address(0)) revert SettlementRetryUnavailable();


        // Check if caller is Settlement owner
        if (msg.sender != settlementReference.owner) {
            if (msg.sender != address(IPort(localPortAddress).getUserAccount(settlementReference.owner))) {
                revert NotSettlementOwner();
            }
        }


        // Update Settlement Status
        settlementReference.status = STATUS_SUCCESS;


        // Perform Settlement Retry
        _performRetrySettlementCall(
            _hasFallbackToggled,
            settlementReference.hTokens,
            settlementRefer
if lzReceiveNonBlocking failed to execute , they don't send back ,accumulated msg.value . malicious user drain that
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L587-L701
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L785-L795
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L730-L753
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L712-L721


# Vulnerability details

## Impact
PerformFallBack is gonna fail and msg.value will be accumulated in that BranchBridgeAgent and malicious user can take over them 

## Proof of Concept
User deposited and When it's not executed ,user want to retrieve and callRetrieveSettlement ,send message to BranchBridgeAgent to retrieveSettlement 

                //DEPOSIT FLAG: 3 (Retrieve Settlement)
        } else if (flag == 0x03) {
            // Parse recipient
            address payable recipient = payable(address(uint160(bytes20(_payload[PARAMS_START:PARAMS_START_SIGNED]))));


            //Get nonce
            nonce = uint32(bytes4(_payload[PARAMS_START_SIGNED:PARAMS_TKN_START_SIGNED]));


            //Check if settlement is in retrieve mode
            if (executionState[nonce] == STATUS_DONE) {
                revert AlreadyExecutedTransaction();
            } else {
                //Set settlement to retrieve mode, if not already set.
                if (executionState[nonce] == STATUS_READY) executionState[nonce] = STATUS_RETRIEVE;
                //Trigger fallback/Retry failed fallback
                _performFallbackCall(recipient, nonce);
            }


_performFallback is executed , 
      
           function _performFallbackCall(address payable _refundee, uint32 _settlementNonce) internal virtual {
        //Sends message to LayerZero messaging layer
        ILayerZeroEndpoint(lzEndpointAddress).send{value: address(this).balance}(
            rootChainId,
            rootBridgeAgentPath,
            abi.encodePacked(bytes1(0x09), _settlementNonce),
            _refundee,
            address(0),
            ""
        );
    }
In _performFallback ,  ILayerZeroEndpoint.send is likely to revert cause there is lack of payable in _refundee or other reason,If they revert ,msg.value that send from RootBridgeAgent will be acummulated in branchBridgeAgent .Malicious user can drain easily them by calling one of execute function like singleSettlement . balance of that BranchBridgeAgent will be sent to Malicious user .

         function _execute(uint256 _settlementNonce, bytes memory _calldata) private {
        //Update tx state as executed
        executionState[_settlementNonce] = STATUS_DONE;


        //Try to execute the remote request
        (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);


        //  No fallback is requested revert allowing for settlement retry.
        if (!success)
Tokens locking due to empty parameters to execute on the root chain router
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L96-L105
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L120-L139


# Vulnerability details

## Vulnerability details
## Impact
User's tokens will be locked at the router contract if the `params` encoded parameters of functions `callOutAndBridge` and `callOutAndBridgeMultiple` are empty. The `executionState[_srcChainId][_depositNonce]` storage variable will have value `STATUS_DONE` and the user won't be able to `retryDeposit`.


## Proof of Concept
The functions `callOutAndBridge` and `callOutAndBridgeMultiple` of the `BranchBridgeAgent` contract are used for direct execution on the root chain router. They receive the `params` encoded parameters which can be executed in the root chain by the root chain router.
The `RootBridgeAgent` contract receives these calls, [writes `STATUS_DONE`](https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L749-L794) at the `executionState[_srcChainId][_depositNonce]` storage variable (the code below for the single deposit case):
```solidity
751        executionState[_srcChainId][_depositNonce] = STATUS_DONE;
752
753        //Try to execute the remote request
754        (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);
755
756        // No fallback is requested revert allowing for retry.
757        if (!success) revert ExecutionFailure();
```
Then tries to `_execute` them via `RootBridgeAgentExecutor` in functions `executeWithDeposit` and `executeWithDepositMultiple` with `_router` address to `_bridgeIn` tokens:
```solidity
96        // Bridge In Assets
97        _bridgeIn(_router, dParams, _srcChainId);
98
99        // Check if there is additional calldata in the payload
100        if (_payload.length > PARAMS_TKN_SET_SIZE) {
101            //Execute remote request
102            IRouter(_router).executeDepositSingle{value: msg.value}(
103                _payload[PARAMS_TKN_SET_SIZE:], dParams, _srcChainId
104            );
105        }
```
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L96-L105
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L120-L140
These functions call `_router` with the parameters or just transfer token to `_router` if parameters are empty. The user can't `retryDeposit` with others parameters or somehow return tokens. Tokens will be locked.
This implementation of the `CoreRootRouter` reverts on calls with parameters, but if parameters are empty tokens will be transfered to the `_router`.


## Tools Used
Manual review


## Recommended Mitigation Steps
Consider checking empty parameters for these types of calls and throw an error.


## Assesse
LACK OF INPUT VALIDATION ON THE `_recipient` ADDRESS IN THE `RootBridgeAgent.retrySettlement` FUNCTION CAN LEAD TO LOSS OF FUNDS TO THE USER
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L233-L239
https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L257-L270
https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchBridgeAgentExecutor.sol#L82-L84
https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchBridgeAgent.sol#L906-L914
https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchBridgeAgentExecutor.sol#L92


# Vulnerability details

## Impact

The `RootBridgeAgent.retrySettlement` function is used to retry the settlement of tokens by the `settlement owner`. The `retrySettlement` function is an external payable function which does not have access control, hence can be called by any `settlement owner` by providing the required input parameters to the function.

In the `retrySettlement` function one of the input parameters expected to passed in is the `_recipient` address. When calling the `_performRetrySettlementCall` function `_recipient` is passed in as the recipient address of the tokens instead of `settlementReference.recipient` address thus allowing the `owner` to change the address of the `recipient`. 

But there is no input validation (no address(0) check) for the `_recipient` address thus enabling the possibility of passing in the default value of `address(0)` as the `_recipient` address. During the execution flow of the `retrySettlement` function the `_recipient` address is not checked for `address(0)` in any of the instances.

Hence when this `retrySettlement` transaction is executed in the `BranchBridgeAgentExecutor.executeWithSettlement` function the following situation could occur:

1. `BranchBridgeAgent.clearToken` function is called which in turn calls the `IPort.bridgeIn` and `IPort.withdraw` function for the `local _hTokens` and underlying `_tokens` respectively.

The `IPort.bridgeIn` function could mint the `local _hTokens` to the `address(0)` if the `_recipient == address(0)` thus losing funds for the user as shown below:

        ERC20hTokenBranch(_localAddress).mint(_recipient, _amount);

The `IPort.withdraw` function could transfer the `underlying tokens` to the `address(0)` if the `_recipient == address(0)` thus losing funds for the user as shown below:

        _underlyingAddress.safeTransfer(_recipient, _deposit); 

2. In the `BranchBridgeAgentExecutor.executeWithSettlement` function if the `_payload.length <= PARAMS_SETTLEMENT_OFFSET` condition occurs then the remaining native / gas token could be sent to the address(0) if the `_recipient == address(0)` as shown below:

            _recipient.safeTransferETH(address(this).balance); 

All above vulnerabilities exist because in the `RootBridgeAgent.retrySettlement` function the `settlement owner` is allowed to pass in the `_recipient` address and no `address(0)` check is performed on the input `_recipient` address. Hence if the `settlement owner` does not pass in the `_recipient` address thinking the `settlement.recipie