Skip to content

Latest commit

 

History

History
51 lines (32 loc) · 3.05 KB

090.md

File metadata and controls

51 lines (32 loc) · 3.05 KB

Calm Basil Lark

Medium

A malicious nouns holder can front-run forwardAll and avoid paying 1 tick to DAO

Summary

A noun owner can wait for the last moment to return his nouns and profit from all benefits one more day by avoiding paying one tick. To do that, the malicious user just has to front run forwardAll() by calling cancelStream() and return his noun before the current tick value is sent to the DAO.

Root Cause

There is no protection logic on cancelStream, it can be called whenever a noun holder wants during a day to return his noun and stop the stream. A malicious user can use his noun one day without having to pay the tick he is supposed to.

Internal pre-conditions

  • attacker holds a noun
  • attacker has a current stream open for this nounId

External pre-conditions

  • someone (likely the DAO) is about to execute forwardAll() which send current tick value to DAO treasury
  • OR settleAuction() is about to be called and will execute forwardAll()

Attack Path

  • attacker front-run forwardAll by calling cancelStream()
  • attacker avoid paying 1 tick even though he kept his noun all day.

Impact

DAO looses one day of tick for a given noun. The malicious user can use his noun to vote/propose and interact with nouns DAO one more day for free.

Historically it happened that Nouns have been sold to 30 ether In current deployment condition, 80 % will go to StreamEscrow.sol -> 26 ETH. And the stream is about 4 years, so 1460 ticks. Meaning 1 ticks = 0.018 ETH = 65$ (with ETH/USD 3600$) One tick = 0.068% of total ticks (1460)

Per Sherlock rules; it's enough to qualify as a Medium :

The protocol loses more than 0.01% and more than $10 of the fees.

Mitigation

It's possible to mitigate with differents approach :

  • add a maximum time after lastForwardTimestamp in cancelStream.
require(lastForwardTimestamp + 50400 < block.timestamp) // 14h as max time given to return the noun

For example, if you don't return your noun 14h (example value, to be chosen by the DAO) after last update of lastForwardTimestamp (forwardAll() call) you have to wait next tick to return it. As forwardAll() is public and will be called almost everyday, not an issue to lock.

  • add a time logic to pay a part of the tick in cancelStream(), meaning user will be refund a fraction of the current tick, depending on the difference between block.timestamp and lastForwardTimestamp