DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Valid

Exploitable Race Condition Between `bid()` and `auctionEnd()` Allowing Malicious Manipulation of Auction Outcome

Summary

The FjordAuction contract is vulnerable to a race condition that can be exploited by a malicious user. This vulnerability arises when a user places a bid (bid()) in the last block of the auction while another user calls auctionEnd() in the same block.

The bid might not be fully processed before the multiplier is calculated in auctionEnd(), leading to an incorrect and potentially manipulable final distribution of the auctioned tokens.

Vulnerability Details

The race condition occurs between the bid() and auctionEnd() functions. An honest user decides at the last moment to call bid(). But in the meantime, someone else calls auctionEnd() at the last moment and the bid is not counted. This is possible because both functions can be called when block.timestamp = auctionEndTime.

function bid(uint256 amount) external {
if (block.timestamp > auctionEndTime) {
revert AuctionAlreadyEnded();
}
bids[msg.sender] = bids[msg.sender].add(amount);
totalBids = totalBids.add(amount);
fjordPoints.transferFrom(msg.sender, address(this), amount);
emit BidAdded(msg.sender, amount);
}
function auctionEnd() external {
if (block.timestamp < auctionEndTime) {
revert AuctionNotYetEnded();
}
if (ended) {
revert AuctionEndAlreadyCalled();
}
ended = true;
emit AuctionEnded(totalBids, totalTokens);
if (totalBids == 0) {
auctionToken.transfer(owner, totalTokens);
return;
}
multiplier = totalTokens.mul(PRECISION_18).div(totalBids);
// Burn the FjordPoints held by the contract
uint256 pointsToBurn = fjordPoints.balanceOf(address(this));
fjordPoints.burn(pointsToBurn);
}

If bid() is executed after auctionEnd() has calculated the multiplier but within the same block, the new bid will not be reflected in the multiplier calculation. This situation could be exploited by a malicious user who bids in the last block to manipulate the auction outcome.

Impact

The multiplier willt be calculated based on a lower totalBids than actually exists, leading to an incorrect distribution of the auctionToken. Users who placed valid bids could receive fewer tokens than they are entitled to, while the final user might benefit unfairly.

Tools Used

Visual Studio Code

Recommendations

To prevent this exploitation, implement a check that disables the bid() function in the final block when the auction is about to end. This ensures that all bids are fully processed before the auction is finalized.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Users can bid in the same block when the actionEnd could be called (`block.timestamp==actionEndTime`), depending on the order of txs in block they could lose funds

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.