The finalize function of the contract SizeSealed is used to finalize an auction, allowing the auctioner (or seller) to be paid quote tokens and also eventually allowing successful bidders to withdraw base tokens.
Once the finalize function is called, the atState modifier check should pass, which ends the finalize state of the function.
However, there are certain cases where the seller can use malicious input and then call cancelAuction, which does not allow bidders to withdraw or refund, effectively locking their tokens within the contract.
bidders will still not be able to access their funds, as the atState modifier will always return that the contract is in the finalize state.
POC
The base token to quote token ratio should be less than or equal to one.
The seller initializes the auction as normal
Bidding also takes place normally
The seller calls the finalize function with the initial ratio, but scaled to type(uint128).max (i.e. base token == initialBase/initialQuote x type(uint128).max and quote token == type(uint128).max)
This will allow the seller to earn funds as usual
The sellercan then call cancelAuction. This locks existing bidder funds in the contract
The reasoning behind this exploit is that the atState modifier for finalize is that quoteToken != type(uint128).max. By setting the two values equal, the check will by bypassed, and atState will always be in the finalize state, which allows the auction to be cancelled, and also prevent existing funds from being withdrawn or refunded.
Lines of code
https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L33-L34 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L391-L410 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L358 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L336
Vulnerability details
Description
The
finalize
function of the contractSizeSealed
is used to finalize an auction, allowing the auctioner (or seller) to be paid quote tokens and also eventually allowing successful bidders to withdraw base tokens.Once the
finalize
function is called, theatState
modifier check should pass, which ends the finalize state of the function.However, there are certain cases where the
seller
can use malicious input and then callcancelAuction
, which does not allowbidders
towithdraw
orrefund
, effectively locking their tokens within the contract.bidders
will still not be able to access their funds, as theatState
modifier will always return that the contract is in thefinalize
state.POC
seller
initializes the auction as normalseller
calls thefinalize
function with the initial ratio, but scaled totype(uint128).max
(i.e. base token == initialBase/initialQuote xtype(uint128).max
and quote token ==type(uint128).max
)seller
to earn funds as usualseller
can then callcancelAuction
. This locks existingbidder
funds in the contractThe reasoning behind this exploit is that the
atState
modifier forfinalize
is thatquoteToken != type(uint128).max
. By setting the two values equal, the check will by bypassed, andatState
will always be in thefinalize
state, which allows the auction to be cancelled, and also prevent existing funds from being withdrawn or refunded.Code:
Output:
As demonstrated, both the
withdraw
andrefund
function calls are reverted, meaning there is no wbidders
to recieve their funds back.Recommended Fix:
Change the
finalize
condition. Using aboolean isFinalized
and setting it to be true or false may mitigate this issue.