code-423n4 / 2024-04-panoptic-findings

8 stars 3 forks source link

Use of delegatecall in a payable function inside a loop #436

Closed c4-bot-2 closed 4 months ago

c4-bot-2 commented 5 months ago

Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/multicall/Multicall.sol#L15-L15

Vulnerability details

Impact

The Multicall contract uses the delegatecall (which takes user-provided calldata) in a payable function within a loop. This means that each delegatecall within the for loop will retain the msg.value of the transaction

Proof of Concept

  function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; ) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Bubble up the revert reason
                // The bytes type is ABI encoded as a length-prefixed byte array
                // So we simply need to add 32 to the pointer to get the start of the data
                // And then revert with the size loaded from the first 32 bytes
                // Other solutions will do work to differentiate the revert reasons and provide paranthetical information
                // However, we have chosen to simply replicate the the normal behavior of the call
                // NOTE: memory-safe because it reads from memory already allocated by solidity (the bytes memory result)
                assembly ("memory-safe") {
                    revert(add(result, 32), mload(result))
                }
            }

            results[i] = result;

            unchecked {
                ++i;
            }
        }
    }

The protocol does not currently use the msg.value in any meaningful way. it could be exploited to tamper with the system arithmetic.

Tools Used

Manual Review

Assessed type

call/delegatecall

c4-judge commented 4 months ago

Picodes marked the issue as unsatisfactory: Invalid