# Attacker can steal any funds in the contract by state confusion (no preconditions)

SIZE auction state machine keyed on lowestQuote; finalize with clearingQuote=uint128.max lets attacker cancel auction and bid, draining contract funds.

- Payout: Contest reward
- Severity: high
- Program: SIZE
- Credited: Trust
- Disclosed: 2023-01-09
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-11-size-findings/issues/252
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-size%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic

**Target:** SizeSealed auction contract (Solidity)
**Root cause:** atState() infers Finalized state from a.data.lowestQuote != type(uint128).max, but finalize() writes lowestQuote from user-controlled clearingQuote, so setting it to uint128.max makes the contract appear un-finalized.
**Steps:**
1. Deploy attacker seller and buyer contracts.
2. Seller createAuction with totalBaseAmount=2**32, reserveQuotePerBase=2**120, minimumBidQuote=2**20, endTimestamp=now+1, no vesting.
3. Buyer bids baseAmount=quoteAmount=2**30 (1:1 ratio).
4. Warp past endTimestamp; seller calls reveal() then finalize([0], 2**128-1, 2**128-1) so lowestQuote=lowestBase=uint128.max.
5. Seller calls cancelAuction(): receives back totalBaseAmount (Y base tokens), endTimestamp set to uint32.max.
6. Buyer calls cancelBid(0): since endTimestamp=uint32.max, check passes, buyer refunded quoteAmount.
7. Net: attacker keeps Y quote tokens drained from contract.
**How found:** Code-read of atState() modifier: noticed Finalized state is derived from lowestQuote rather than a dedicated flag, and finalize() sets lowestQuote from user-supplied clearingQuote. Then worked through inequality constraints (previousQuotePerBase equality, reserve check, minimumBidQuote) to find parameters making the full flow succeed.
**Exploitation:** Attacker controls both seller and buyer contracts. Finalize with clearingQuote=clearingBase=uint128.max satisfies the previousQuotePerBase equality check (quoteAmount=baseAmount, clearingBase=uint128.max). Finalize transfers unsold base and filled quote to seller; then cancelAuction refunds base and sets endTimestamp=uint32.max, enabling cancelBid to refund the quote. Attacker nets the quote tokens still held by the auction contract.
**Escalation:** Parameter tuning (minimumBidQuote, reserveQuotePerBase, totalBaseAmount) lets attacker scale stolen amount; POC steals 2**30 quote tokens (~1000 USDC).
**Insight:** When a state machine derives state from a value that user input can write, look for sentinel values (uint128.max) that flip the derived state back; here finalize()'s clearingQuote is attacker-chosen and equals the 'not finalized' sentinel.
