Bid can be placed after the auction has ended, allowing a malicious user to exploit the multiplier calculation and withdraw more tokens than they should have received at the expense of other users.
At the end of the auction, the system calculates a multiplier using the formula totalTokens / totalBids
, which is then stored as a state variable. This multiplier
determines how many tokens each bidder receives based on their bids. Typically, with each subsequent bid, this multiplier should decrease.
However, when block.timestamp
equals auctionEndTime
and ended
is set to true
, the contract still allows users to place bids. A malicious actor can exploit this by first calling auctionEnd
to lock in the multiplier
, and then placing a bid
within the same block. This bid is accepted without adjusting the multiplier, allowing the malicious user to claim more tokens than their fair share, leaving fewer tokens for legitimate bidders.
Consider a scenario when:
Alice is the sole bidder, placing a bid of 100 fjordPoints for 100 auction tokens.
As the auction ends (when block.timestamp
equals auctionEndTime
), Bob (a malicious user) calls auctionEnd. The system calculates a multiplier of 1e18 (totalTokens / totalBids).
Immediately after, within the same block, Bob places a bid of 100 fjordPoints. Despite the bid, the multiplier remains 1e18.
Bob then calls claimTokens
, withdrawing 100 auction tokens. As a result, Alice is left with no tokens to withdraw, despite her legitimate bid.
This vulnerability results in an unfair token distribution, allowing malicious actors to disproportionately benefit at the expense of legitimate participants. This can lead to significant financial losses for honest bidders who receive fewer tokens than expected or none at all.
Manual
Ensure that users cannot place bids after the auction has ended by modifying the bid
function to include a stricter time check. Specifically, update the condition to check if block.timestamp
is greater than or equal to auctionEndTime
, or validate using the ended
flag.
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.