Skip to content

Latest commit

 

History

History
78 lines (55 loc) · 2.2 KB

066.md

File metadata and controls

78 lines (55 loc) · 2.2 KB

Lone Carob Worm

Medium

Malicious actor can DOS auction settlement via frontrunning StreamEscrow.forwardAllAndCreateStream

Summary

Missing access control in StreamEscrow.forwardAllAndCreateStream allows frontrunning of auction settlement, preventing NFT transfer to winning bidder https://github.com/sherlock-audit/2024-11-nounsdao/blob/main/nouns-monorepo/packages/nouns-contracts/contracts/NounsAuctionHouseV3.sol#L341

https://github.com/sherlock-audit/2024-11-nounsdao/blob/main/nouns-monorepo/packages/nouns-contracts/contracts/StreamEscrow.sol#L113-L115

Root Cause

In NounsAuctionHouseV3.sol:_settleAuction() calls StreamEscrow.forwardAllAndCreateStream which lacks auction-only access control, allowing frontrunning attacks

Internal pre-conditions

  1. Auction has ended with winning bid
  2. Attacker needs to be approved operator or owner of the noun
  3. Attacker needs to be in allowedToCreateStream mapping

External pre-conditions

No response

Attack Path

  1. Auction ends with winning bid
  2. _settleAuction() is called
  3. Attacker front-runs the settlement transaction by calling forwardAllAndCreateStream with same nounId
  4. Original settlement transaction will revert on 'stream active' check
  5. Winning bidder cannot receive their NFT

Impact

The winning bidder cannot receive their NFT, effectively DOSing the auction settlement process and potentially locking funds

PoC

function testFrontrunningAttack() public {
    // Setup
    address attacker = address(0x1);
    uint256 nounId = 1;
    
    // Give attacker required permissions
    streamEscrow.setAllowedToCreateStream(attacker, true);
    nouns.approve(attacker, nounId);
    
    // Auction ends
    // Attacker frontruns settlement
    vm.prank(attacker);
    streamEscrow.forwardAllAndCreateStream{value: 1 ether}(nounId, 5);
    
    // Settlement will revert
    vm.expectRevert("stream active");
    auctionHouse._settleAuction();
}

Mitigation

modifier onlyAuction() {
    require(msg.sender == address(auction), "only auction");
    _;
}

function forwardAllAndCreateStream(uint256 nounId, uint16 streamLengthInTicks) 
    external 
    payable 
    onlyAuction 
{
    // ... existing code
}