Summary
The FjordAuction contract is vulnerable to an integer overflow in the calculation of the multiplier during the auctionEnd process. This overflow occurs due to unchecked arithmetic operations when calculating the multiplier with extremely large values of totalTokens and very small totalBids. This flaw can lead to incorrect token distribution and potential financial discrepancies.
uint256 claimable = userBids.mul(multiplier).div(PRECISION_18);
Vulnerability Details
https://github.com/Cyfrin/2024-08-fjord/blob/0312fa9dca29fa7ed9fc432fdcd05545b736575d/src/FjordAuction.sol#L207-L222
1.Contract Setup:
2.Multiplier Calculation:
multiplier = (totalTokens * PRECISION_18) / totalBids;
multiplier = ((2^256 - 1) * 10^18) / 1;
3.Triggering the Vulnerability:
4.Expected Behavior:
pragma solidity =0.8.21;
import "forge-std/Test.sol";
import "../src/FjordAuction.sol";
import "./CustomERC20.sol";
contract FjordAuctionTest is Test {
FjordAuction auction;
CustomERC20 fjordPoints;
CustomERC20 auctionToken;
address owner = address(0x123);
address user = address(0x456);
function setUp() public {
fjordPoints = new CustomERC20("FjordPoints", "FJP");
auctionToken = new CustomERC20("AuctionToken", "AUC");
auction = new FjordAuction(address(fjordPoints), address(auctionToken), 1 days, type(uint256).max);
vm.startPrank(owner);
fjordPoints.mint(address(auction), 1);
auctionToken.mint(address(auction), type(uint256).max);
vm.stopPrank();
}
function testMultiplierCalculation() public {
vm.startPrank(owner);
fjordPoints.mint(owner, 1);
fjordPoints.approve(address(auction), 1);
auction.bid(1);
vm.stopPrank();
vm.warp(block.timestamp + 1 days);
try auction.auctionEnd() {
assertTrue(false, "Expected overflow error not thrown");
} catch (bytes memory ) {
assertTrue(true);
}
}
}
forge test --match-path test/FjordAuctionTest.t.sol -vvvv
[⠊] Compiling...
[⠒] Compiling 1 files with Solc 0.8.21
[⠢] Solc 0.8.21 finished in 2.00s
Compiler run successful!
Ran 1 test for test/FjordAuctionTest.t.sol:FjordAuctionTest
[PASS] testMultiplierCalculation() (gas: 123940)
Traces:
[160190] FjordAuctionTest::testMultiplierCalculation()
├─ [0] VM::startPrank(0x0000000000000000000000000000000000000123)
│ └─ ← [Return]
├─ [29792] CustomERC20::mint(0x0000000000000000000000000000000000000123, 1)
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000123, value: 1)
│ └─ ← [Stop]
├─ [24652] CustomERC20::approve(FjordAuction: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a], 1)
│ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000123, spender: FjordAuction: [0xF62849F9A0B5Bf2913b396098F7c701
9b51A820a], value: 1) │ └─ ← [Return] true
├─ [61512] FjordAuction::bid(1)
│ ├─ [10529] CustomERC20::transferFrom(0x0000000000000000000000000000000000000123, FjordAuction: [0xF62849F9A0B5Bf2913b396098F7
c7019b51A820a], 1) │ │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000123, spender: FjordAuction: [0xF62849F9A0B5Bf2913b396098F7
c7019b51A820a], value: 0) │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000123, to: FjordAuction: [0xF62849F9A0B5Bf2913b396098F7c7019b
51A820a], value: 1) │ │ └─ ← [Return] true
│ ├─ emit BidAdded(bidder: 0x0000000000000000000000000000000000000123, amount: 1)
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::warp(86401 [8.64e4])
│ └─ ← [Return]
├─ [26560] FjordAuction::auctionEnd()
│ ├─ emit AuctionEnded(totalBids: 1, totalTokens: 11579208923731619542357098500868790785326998466564056403945758400791312963993
5 [1.157e77]) │ └─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
├─ [0] VM::assertTrue(true) [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 699.30µs (122.60µs CPU time)
Ran 1 test suite in 5.52ms (699.30µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
Impact
Due to the overflow, the multiplier calculation fails, leading to incorrect or failed distribution of auction tokens to bidders.
Users may receive incorrect amounts of tokens, resulting in financial discrepancies and potential loss of trust in the auction mechanism.
The overflow causes the contract to revert unexpectedly, disrupting the normal operation and usability of the contract.
Tools Used
Recommendations
Ensure that totalBids is not too small relative to totalTokens to prevent unrealistic multiplier values. Implement checks to validate that totalBids is above a certain threshold before performing the calculation.
Set a reasonable cap on totalTokens to prevent extreme values that could lead to overflow. This can be done by introducing a maximum limit for totalTokens during the contract setup or auction initialization.
Before performing the multiplication, check if the result will exceed uint256 limits. This can be done by rearranging the calculation to avoid large intermediate results or by implementing logic to detect potential overflow conditions.