Skip to content

Latest commit

 

History

History
100 lines (66 loc) · 4 KB

001.md

File metadata and controls

100 lines (66 loc) · 4 KB

Flat Tartan Mantis

High

vah - Reentrancy Vulnerability in StreamEscrow Contract

Summary

A reentrancy vulnerability exists in the StreamEscrow contract within the cancelStream and sendETHToTreasury functions. These functions perform external calls (ETH transfers) without appropriate reentrancy protection, allowing an attacker to perform a reentrancy attack and manipulate the contract's state, potentially leading to loss of funds or other unintended behaviors.

Root Cause

Location Contract: StreamEscrow.sol Contract: Functions: cancelStream, sendETHToTreasurysendETHToTreasury

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. Deploy malicious contract
    The attacker deploys a contract named ReentrancyAttack that contains a fallback function designed to re-enter the cancelStream function of the StreamEscrow contract.

  2. Create an active stream
    The attacker ensures they have an active stream by calling createStream on the StreamEscrow contract with their nounId and sending the required ETH to initiate the stream.

  3. Approve Noun token transfer
    The attacker calls nounsToken.approve(StreamEscrowAddress, nounId) to allow the StreamEscrow contract to transfer their Noun token on their behalf.

  4. Initiate the attack
    The attacker calls the attack function on their ReentrancyAttack contract to begin the exploitation process.

  5. First call to cancelStream
    Inside the attack function, the ReentrancyAttack contract calls the cancelStream function on the StreamEscrow contract, initiating the stream cancellation process.

  6. Transfer of Noun token
    The StreamEscrow contract transfers the Noun token from the attacker to the nounsRecipient address as part of the stream cancellation process.

  7. State update deferred
    The StreamEscrow contract schedules updates to the stream's state variables but does not apply them before making an external call.

  8. External call triggers fallback
    The StreamEscrow contract attempts to send ETH back to the attacker's contract using msg.sender.call{ value: amountToRefund }(''), triggering the fallback function in the attacker's contract.

  9. Re-enter cancelStream
    The fallback function in the ReentrancyAttack contract re-enters the cancelStream function before the state variables are updated, allowing the attacker to repeat the cancellation process.

  10. Repeat cancellation
    Steps 5–9 are repeated multiple times, enabling the attacker to cancel the same stream multiple times and receive the refund each time.

  11. Drain funds
    Through repeated reentrancy, the attacker drains ETH from the StreamEscrow contract, exploiting the vulnerability and depleting its balance.

Impact

The contract suffers a significant loss of ETH equal to multiple times the refund amount of the canceled stream.

PoC

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

import "./StreamEscrow.sol";

contract ReentrancyAttack {
    StreamEscrow public target;
    uint256 public nounId;

    constructor(address _target, uint256 _nounId) {
        target = StreamEscrow(_target);
        nounId = _nounId;
    }

    fallback() external payable {
        if (address(target).balance >= 1 ether) {
            // Re-enter the cancelStream function
            target.cancelStream(nounId);
        }
    }

    function attack() external payable {
        // Assume the attacker has an active stream for nounId
        // and has approved the transfer of the Noun token to the target contract

        // Cancel the stream, triggering the reentrancy attack
        target.cancelStream(nounId);
    }
}

Mitigation

No response