Maia DAO - Ulysses - T1MOH's results

Harnessing the power of Arbitrum, Ulysses Omnichain specializes in Virtualized Liquidity Management.

General Information

Platform: Code4rena

Start Date: 22/09/2023

Pot Size: $100,000 USDC

Total HM: 15

Participants: 175

Period: 14 days

Judge: alcueca

Total Solo HM: 4

Id: 287

League: ETH

Maia DAO

Findings Distribution

Researcher Performance

Rank: 72/175

Findings: 2

Award: $39.31

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L85

Vulnerability details

Impact

payableCall() misses modifier requiresApprovedCaller. As a result anyone can perform any call from VirtualAccount. For example it can be ERC20/ERC721 transfer.

Proof of Concept

I really don't see need to prove such an obvious issue with executable test.

Anyone can specify arbitrary target and calldata and perform call from VirtualAccount:

    function payableCall(PayableCall[] calldata calls) public payable returns (bytes[] memory returnData) {
        uint256 valAccumulator;
        uint256 length = calls.length;
        returnData = new bytes[](length);
        PayableCall calldata _call;
        for (uint256 i = 0; i < length;) {
            _call = calls[i];
            uint256 val = _call.value;
            // Humanity will be a Type V Kardashev Civilization before this overflows - andreas
            // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256
            unchecked {
                valAccumulator += val;
            }

            bool success;

@>          if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData);

            if (!success) revert CallFailed();

            unchecked {
                ++i;
            }
        }

        // Finally, make sure the msg.value = SUM(call[0...i].value)
        if (msg.value != valAccumulator) revert CallFailed();
    }

Tools Used

Manual Review

Add modifier requiresApprovedCaller

Assessed type

Invalid Validation

#0 - c4-pre-sort

2023-10-08T14:33:55Z

0xA5DF marked the issue as duplicate of #888

#1 - c4-pre-sort

2023-10-08T14:41:01Z

0xA5DF marked the issue as sufficient quality report

#2 - c4-judge

2023-10-26T11:32:35Z

alcueca marked the issue as satisfactory

Findings Information

Labels

bug
2 (Med Risk)
downgraded by judge
partial-50
sufficient quality report
duplicate-348

Awards

39.2026 USDC - $39.20

External Links

Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L1212-L1214

Vulnerability details

Impact

LayerZero passes argument bytes calldata _srcAddress. According to documentation it contains 40 bytes, actually it is 2 addresses concatenated. You can check detail explanation in according documentation

It encodes remoteAddress + localAddress. Where remoteAddress is caller on source chain, and localAddress is contract on destination chain - in other words address(this).

Problem is that it decodes localAddress and compares to trusted caller instead of remoteAddress. As a result RootBridgeAgent can't receive messages from other chains

Proof of Concept

Firstly docs explain in clear manner that 20-40 bytes is address(this)

However I want to provide Tenderly debug to prove this. Take a look on Endpoint's retryPayload() on Tenderly Function Endpoint.retryPayload() is fallback used to execute cross-chain message. Submitted _srcAddress = 0x3055913c90fcc1a6ce9a358911721eeb942013a1152649ea73beab28c5b49b26eb48f7ead6d4c898 - it means:

  1. remoteAddress = 0x3055913c90fcc1a6ce9a358911721eeb942013a1
  2. localAddress = 0x152649ea73beab28c5b49b26eb48f7ead6d4c898

In this tx Endpoint calls ERC20 token CakeOFT to mint tokens. Address of this token is indeed this localAddress, check this url. It proves point that sender of message is encoded in first 20 bytes of _srcAddress

However you can see that RootBridgeAgent tries to read the last 20 bytes:

    modifier requiresEndpoint(address _endpoint, uint16 _srcChain, bytes calldata _srcAddress) virtual {
        if (msg.sender != address(this)) revert LayerZeroUnauthorizedEndpoint();

        if (_endpoint != getBranchBridgeAgent[localChainId]) {
            if (_endpoint != lzEndpointAddress) revert LayerZeroUnauthorizedEndpoint();

            if (_srcAddress.length != 40) revert LayerZeroUnauthorizedCaller();

@>          if (getBranchBridgeAgent[_srcChain] != address(uint160(bytes20(_srcAddress[PARAMS_ADDRESS_SIZE:])))) {
                revert LayerZeroUnauthorizedCaller();
            }
        }
        _;
    }

Tools Used

Manual Review

Refactor check:

    modifier requiresEndpoint(address _endpoint, uint16 _srcChain, bytes calldata _srcAddress) virtual {
        if (msg.sender != address(this)) revert LayerZeroUnauthorizedEndpoint();

        if (_endpoint != getBranchBridgeAgent[localChainId]) {
            if (_endpoint != lzEndpointAddress) revert LayerZeroUnauthorizedEndpoint();

            if (_srcAddress.length != 40) revert LayerZeroUnauthorizedCaller();

-           if (getBranchBridgeAgent[_srcChain] != address(uint160(bytes20(_srcAddress[PARAMS_ADDRESS_SIZE:])))) {
+           if (getBranchBridgeAgent[_srcChain] != address(uint160(bytes20(_srcAddress[0:PARAMS_ADDRESS_SIZE])))) {
                revert LayerZeroUnauthorizedCaller();
            }
        }
        _;
    }

Assessed type

Invalid Validation

#0 - c4-pre-sort

2023-10-13T06:29:35Z

0xA5DF marked the issue as duplicate of #439

#1 - c4-pre-sort

2023-10-13T06:29:40Z

0xA5DF marked the issue as sufficient quality report

#2 - c4-judge

2023-10-26T09:42:12Z

alcueca changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-10-26T09:48:32Z

alcueca marked the issue as satisfactory

#4 - alcueca

2023-10-26T09:51:37Z

There are very high effort submissions in this duplicate group. All others are getting 50% so that the few very high effort ones get double rewards.

#5 - c4-judge

2023-10-26T09:51:43Z

alcueca marked the issue as partial-50

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter