The vulnerability stems from the timing checks in the bid() and auctionEnd() functions. Here are the full code snippets for both functions:
See: FjordAuction.bid and FjordAuction.auctionEnd
The key issue lies in the timing checks:
The bid() function reverts if block.timestamp exceeds auctionEndTime.
The auctionEnd() function reverts if block.timestamp is less than auctionEndTime.
That means, both functions accept transactions when block.timestamp == auctionEndTime. This creates a situation where auctionEnd() can be called to finalize the auction, but bid() can still be called in the same block.
The problem arises because the multiplier for token distribution is set when auctionEnd() is called, but additional bids can still be placed afterwards. This discrepancy leads to the following issues:
The auctionEnd() function calculates the multiplier based on the current totalBids and burns all FjordPoints held by the contract.
However, additional bids can still be placed in the same block after auctionEnd() is called.
These late bids increase caller's bids without adjusting the multiplier, leading to an unfair distribution of auction tokens.
The auction has been running with a total of 1,000,000 FjordPoints bid for 100,000 auction tokens.
At auctionEndTime, auctionEnd() is called, setting the multiplier to 0.1 (100,000 * 1e18 / 1,000,000).
In the same block, a malicious actor calls bid() with 1,000,000 FjordPoints.
The malicious actor then immediately calls claimTokens().
The malicious actor receives 100,000 tokens (1,000,000 * 0.1).
When honest bidders try to claim their tokens, there are insufficient tokens left in the contract.
This scenario demonstrates how the vulnerability can be exploited to drastically skew the auction results, potentially leaving honest bidders with significant losses.
The following test demonstrates the following scenario:
Honest bidder bids 100e18 points for 1000e18 auction tokens
block.timestamp is at exactly auctionEndTime
Malicious actor calls auctionEnd() and bids 100e18 points in the same block
Malicious actors immediately claims all auction tokens
Honest bidder fails to claim his auction tokens because there is no auction tokens left in the contract
Steps
Create a new file, cheat.auction.t.sol, in 2024-08-fjord/test/unit/ and paste the following test.
Run forge t --match-contract TestCheatAuction -vv
Observe that honest bidder claimTokens() reverts from ERC20: transfer amount exceeds balance
This vulnerability can be exploited to manipulate the auction results:
The last honest bidders might not be able to claim their auction tokens.
In the worst-case scenario, a malicious bidder could claim all auction tokens, leaving honest bidders with no tokens and losing their bids.
Despite the impact of direct fund loss, there exists a constraint where auctionEndTime must be equal to block.timestamp which is not attacker-controlled.
Hence, Medium severity.
To fix this vulnerability, consider the following options:
Modify the timing checks in bid() and auctionEnd() to use strict inequality:
Implement a "grace period" after auctionEndTime during which auctionEnd() can be called, but no new bids are accepted.
By implementing one or more of these recommendations, the contract can ensure fair distribution of auction tokens and prevent exploitation of the current race condition.
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.