Skip to content

Latest commit

 

History

History
68 lines (40 loc) · 2.28 KB

098.md

File metadata and controls

68 lines (40 loc) · 2.28 KB

Slow Lipstick Hare

Medium

Permanent Locking of Excess ETH in NounsDAO StreamEscrow Contract that might have been sent by mistake.

Severity: Medium

Vulnerability Context

Contract: StreamEscrow

Root Cause: No callable function to rescue any amount of Excess ETH trapped in the contract.

Impact:

Excess ETH sent directly to the contract will be permanently locked with no mechanism for withdrawal.

Proof of Concept:

  1. The contract does not implement a "rescueETH()" function as it does a "rescueToken" function, so we this implies it is expected that Tokens or the Native Token might be sent to the contract by accident.

See as rescueToken() is already implemented: https://github.com/sherlock-audit/2024-11-nounsdao/blob/main/nouns-monorepo/packages/nouns-contracts/contracts/StreamEscrow.sol#L292

  1. Stream calculations are precise and do not account for excess ETH

  2. The contract is non-upgradeable

  3. No administrative function exists to withdraw accidentally sent ETH

Historical Precedent:

A similar vulnerability was identified in the NounsDAO Stream contract two years ago:

sherlock-audit/2022-11-nounsdao-judging#14

  • The previous Stream contract also lacked a native token rescue mechanism
  • This finding was confirmed and classified as a medium severity issue

Technical Details:

  • Streams are calculated with exact ETH amounts per tick
  • "sendETHToTreasury()" only sends calculated stream amounts
  • No function allows withdrawal of unaccounted ETH balance
  • Contract is non-upgradeable, making the issue permanently unfixable

Potential Loss:

Any ETH sent directly to the contract outside the stream mechanism will be irretrievably lost, potentially leading to permanent fund lockup.

Recommendation:

Implement a "rescueETH()" function similar to "rescueToken()", controlled by the DAO, to allow recovery of mistakenly sent funds.

function rescueETH(address to, uint256 amount) external onlyDAO {
    (bool sent, ) = to.call{value: amount}("");
    require(sent, "ETH transfer failed");
}

Risk Mitigation:

  • Add a function to rescue native tokens
  • Ensure the function is protected by the "onlyDAO" modifier
  • Implement proper transfer checks