sherlock-audit / 2023-02-fair-funding-judging

1 stars 0 forks source link

ABA - Auction can be force started and first token force minted by calling `settle()` before the auction was launched #83

Closed github-actions[bot] closed 1 year ago

github-actions[bot] commented 1 year ago

ABA

high

Auction can be force started and first token force minted by calling settle() before the auction was launched

Summary

Auction can be force started and first token force minted by calling settle() before the auction was launched

Vulnerability Detail

The settle() function is used to finalize a finished auction (mint the won NFT to the winner) and send the won bid amount to the vault.

The function itself, is callable by anybody, the only restriction it has is that it reverts if an auction epoch is running, by checking _epoch_in_progress() https://github.com/sherlock-audit/2023-02-fair-funding/blob/main/fair-funding/contracts/AuctionHouse.vy#L185

This check however only verifies that this function is not executed during an auction. There is no check if the auction has actually started, thus all conditions are met.

The logical execution flow is as follows:

Another issue that arrise from the above execution is that the auction itself is force started: https://github.com/sherlock-audit/2023-02-fair-funding/blob/main/fair-funding/contracts/AuctionHouse.vy#L202-L203

Impact

Code Snippet

The following test test_settle_called_before_start_auction_issue can be added in test_AuctionHouse_settle.py to show that, the code is in fact vulnerable.

def test_settle_called_before_start_auction_issue(owner, house, nft):
    # no house start_auction!
    token_id = house.current_epoch_token_id()
    default_epoch_start = house.epoch_start()

    assert default_epoch_start == 0  # this validates that the default epoch start is 0 if auction not started

    with boa.reverts():  # invalid token id, owner == 0x0
        nft.ownerOf(token_id)  # this validates that the token_id does not have an owner as of now

    house.settle()

    assert nft.ownerOf(token_id) == owner # this validates that the NFT was minted to the fallback address

    epoch_start_after_settle = house.epoch_start()

    assert epoch_start_after_settle != pytest.ZERO_ADDRESS # this validates that the auction was also started

Tool used

Manual Review

Recommendation

Add an auction_started flag requirement in settle(). This flag would only be set in the start_auction function. Example implementation:

diff --git a/fair-funding/contracts/AuctionHouse.vy b/fair-funding/contracts/AuctionHouse.vy
index fe470ee..79b56f4 100644
--- a/fair-funding/contracts/AuctionHouse.vy
+++ b/fair-funding/contracts/AuctionHouse.vy
@@ -56,6 +56,8 @@ max_token_id: public(uint256)
 highest_bid: public(uint256)
 highest_bidder: public(address)

+auction_started: public(bool)
+
 epoch_start: public(uint256)
 epoch_end: public(uint256)

@@ -183,6 +185,7 @@ def settle():
         Resets everything and starts the next epoch / auction.
     """
     assert self._epoch_in_progress() == False, "epoch not over"
+    assert self.auction_started == True, "auction has not started yet"

     winner: address = self.highest_bidder
     token_id: uint256 = self.current_epoch_token_id
@@ -237,6 +240,7 @@ def start_auction(_start_time: uint256):

     self.epoch_start = start
     self.epoch_end = self.epoch_start + EPOCH_LENGTH
+    self.auction_started = True

     log AuctionStart(self.epoch_start, msg.sender)
     log Bid(0, msg.sender, 123)

Duplicate of #39