reNFT - plasmablocks's results

Collateral-free, permissionless, and highly customizable EVM NFT rentals.

General Information

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

reNFT

Findings Distribution

Researcher Performance

Rank: 32/116

Findings: 3

Award: $309.96

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: plasmablocks

Also found by: 0xabhay, BARW, hals, serial-coder

Labels

bug
2 (Med Risk)
primary issue
satisfactory
selected for report
sponsor acknowledged
edited-by-warden
M-03

Awards

295.5823 USDC - $295.58

External Links

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L733-L775

Vulnerability details

Impact

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:

  • The call to stop a rental order uses more gas than the call to create a rental order
  • The call to create the rental order is successful
  • The call to stop a rental order uses more gas than the block gas limit

Proof of Concept

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);
    }

Tools Used

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.

Assessed type

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

Awards

1.8029 USDC - $1.80

Labels

2 (Med Risk)
satisfactory
duplicate-239

External Links

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

Awards

12.582 USDC - $12.58

Labels

bug
grade-b
QA (Quality Assurance)
sufficient quality report
edited-by-warden
Q-03

External Links

Contracts use solidity compiler version 0.8.20 which is currently not supported on L2's

The 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

RentalOrder hashes do not include rentalWallet leading to possible hash collisions

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

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L162-L195

#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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter