The SizeSealed contract's cancelAuction() function can be called after auction has been finalized, thereby allowing the seller to withdraw both quoteToken during finalization and baseToken after finalization.
This is possible by bypassing the if (a.data.lowestQuote != type(uint128).max) statement by allowing a.data.lowestQuote to be set to type(uint128).max during the finalize() call where clearingQuote and clearingBase are user controlled. With the control of these arguments in the clearing price calculation during finalize(), the seller can manipulate the final data.previousQuotePerBase value by using a different address to place the last bid such that the bid's quoteAmount would pass all checks.
With all this inputs provided, it would be possible for a seller to finalize the auction, receive quoteTokens for the auction, then call cancelAuction() to receive back the baseTokens deposited for the auction, leaving bidders rugged.
Proof of Concept
Alice sets up an auction
Just before the end of the bid, Alice places the last bid using another address with a crafted bid quiteAmount.
Auction ends and Alice calls finalize() with user supplied input for clearingQuote set to type(uint128).max and clearingBase set to an appropriate number.
Where no overflow reverts occur, finalize() is completed successfully with Alice receiving the calculated amount of quoteTokens for the auction.
Alice then calls cancelAuction() right away.
Since the a.data.lowestQuote is set to type(uint128).max , the if-statement is bypassed and Alice receives back the baseToken she deposited for the auction.
Tools Used
Manual review
Recommended Mitigation Steps
Apply a logic that checks the Auction State, the atState modifier could be applied to ensure the auction is in the AcceptingBids state for the call.
Lines of code
https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L391-L410 https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L238
Vulnerability details
Impact
The SizeSealed contract's cancelAuction() function can be called after auction has been finalized, thereby allowing the seller to withdraw both quoteToken during finalization and baseToken after finalization.
This is possible by bypassing the
if (a.data.lowestQuote != type(uint128).max)
statement by allowinga.data.lowestQuote
to be set totype(uint128).max
during the finalize() call whereclearingQuote
andclearingBase
are user controlled. With the control of these arguments in the clearing price calculation during finalize(), the seller can manipulate the finaldata.previousQuotePerBase
value by using a different address to place the last bid such that the bid's quoteAmount would pass all checks.With all this inputs provided, it would be possible for a seller to finalize the auction, receive quoteTokens for the auction, then call
cancelAuction()
to receive back the baseTokens deposited for the auction, leaving bidders rugged.Proof of Concept
clearingQuote
set to type(uint128).max andclearingBase
set to an appropriate number.a.data.lowestQuote
is set totype(uint128).max
, the if-statement is bypassed and Alice receives back the baseToken she deposited for the auction.Tools Used
Manual review
Recommended Mitigation Steps
Apply a logic that checks the Auction State, the
atState
modifier could be applied to ensure the auction is in the AcceptingBids state for the call.