A user can bid even after the auction ends, which leads to other bidders losing their claimable auction tokens.
The FjordAuction contract facilitates the auctioning of auctionToken in exchange for FjordPointsToken. Users can place bids within the active auction period using their points tokens. If they change their minds before the auction ends, they can withdraw their bids and retrieve their points tokens.
After the auction period ends, bidders can claim their share of auction tokens based on the proportion of their bids.
However, a critical vulnerability exists: the contract allows users to place bids even after the auction has officially ended, which can severely impact other bidders.
The auction can be ended when block.timestamp >= auctionEndTime.
From this snippet, it is clear that a user can still place a bid when block.timestamp == auctionEndTime. This opens up an exploit scenario:
Assume auctionEndTime = x.
Various bidders have placed their bids before time x.
At timestamp x, Bob simultaneously calls auctionEnd() and places a bid for the necessary amount to acquire all the auction tokens.
Since the formula to calculate the claimable tokens is uint256 claimable = userBids.mul(multiplier).div(PRECISION_18);, Bob's bid should be total auctionToken * PRECISION_18 / multiplier.
Bob then calls the claimTokens() function and claims all the auction tokens, leaving other bidders without any claimable tokens.
Poc :
Logs :
"idders will lose their pointsTokens because these tokens are burned when auctionEnd() is called, and the auction tokens they were bidding for are no longer available to claim. Similarly an user can unbid his tokens even after the auction ended.
Manual
Do not allow bids when block.timestamp >= auctionEndTime.
The protocol doesn't properly treat the `block.timestamp == auctionEndTime` case. Impact: High - There are at least two possible impacts here: 1. By chance, user bids could land in a block after the `auctionEnd()` is called, not including them in the multiplier calculation, leading to a situation where there are insufficient funds to pay everyone's claim; 2. By malice, where someone can use a script to call `auctionEnd()` + `bid(totalBids)` + `claimTokens()`, effectively depriving all good faith bidders from tokens. Likelihood: Low – The chances of getting a `block.timestamp == auctionEndTime` are pretty slim, but it’s definitely possible.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.