reNFT - CipherSleuths'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: 13/116

Findings: 4

Award: $1,050.34

🌟 Selected for report: 1

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: CipherSleuths

Also found by: BARW

Labels

bug
2 (Med Risk)
downgraded by judge
insufficient quality report
primary issue
satisfactory
selected for report
sponsor confirmed
edited-by-warden
M-05

Awards

1013.6566 USDC - $1,013.66

External Links

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L265 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100

Vulnerability details

Impact

if an order involves erc777 token for a pay order then in the tokensReceived callback the renter can create DOS situation resulting in the lender's assets being stuck in the rental safe

Proof of Concept

ERC777 token standard which is backward compatible with erc20 implies that on the transfer of the tokens the recipient can implement a tokensReceived hook to notify of any increment of the balance.

Now suppose a pay order is created with an erc 777 consideration asset as there is no restriction on that and also the eip specifies that

The difference for new contracts implementing ERC-20 is that tokensToSend and tokensReceived hooks take precedence over ERC-20. Even with an ERC-20 transfer and transferFrom call, the token contract MUST check via ERC-1820 if the from and the to address implement tokensToSend and tokensReceived hook respectively. If any hook is implemented, it MUST be called. Note that when calling ERC-20 transfer on a contract, if the contract does not implement tokensReceived, the transfer call SHOULD still be accepted even if this means the tokens will probably be locked.

so the tokensReceived hook is optional for a transfer/transferFrom call. Hence sending the assets from the lender's wallet to the escrow contract shouldn't be an issue.

Now when the rental period is over or in/between, the stopRent method in stop policy is called, which calls settlePayment in escrow module. Now on the token transfer

(bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transfer.selector, to, value) );

the tokensReceived hook if implemented by the renter, would be called and they could just revert the tx inside the tokensReceived hook which would mean that the assets lent by the lender are locked forever.

Tools Used

manual review

It is recommended to prohibit erc777 tokens from being used as consideration items.

Assessed type

DoS

#0 - c4-pre-sort

2024-01-21T18:21:03Z

141345 marked the issue as insufficient quality report

#1 - c4-judge

2024-01-26T17:49:57Z

0xean marked the issue as unsatisfactory: Insufficient quality

#2 - c4-judge

2024-01-27T17:39:17Z

0xean marked the issue as duplicate of #65

#3 - c4-judge

2024-01-28T19:23:24Z

0xean marked the issue as satisfactory

#4 - c4-judge

2024-01-28T19:43:32Z

0xean marked the issue as not a duplicate

#5 - c4-judge

2024-01-28T19:45:12Z

0xean marked the issue as duplicate of #462

#6 - c4-judge

2024-01-28T20:48:01Z

0xean changed the severity to 2 (Med Risk)

#7 - udsene

2024-01-30T09:05:43Z

@0xean,

Thank you very much for judging this contest. This issue is marked as a duplicate of issue #462. This issue describes a different vulnerability that could arise as a result of using ERC777 tokens for a PAY order.

The issue here is that once the PAY order is fulfilled the renter can opt not use the NFT to play a game or for any other reason thus the lender will not get any benefit from lending out his NFT. Hence the lender can decide to cancel the PAY order immediately by calling the stopRent method in stop policy. This will call the settlePayment in escrow module

But the malicious renter can use the tokensReceived hook to revert the transaction thus locking the lender's asset permanently since it is not possible for the lender to cancel the rent of the PAY order. The renter is not losing anything here since he did not invest his time to play using the NFT and he doesn't deserve any payment as a result.

The documentation of the contest did not mention that the ERC777 is out of scope and this vulnerability shows how the use of ERC777 hooks can prompt loss of funds (ERC721 NFT) to the lender when using a PAY order.

Hence i think the impact of this issue deserves a medium severity.

Thank You

#8 - 0xean

2024-01-30T15:09:26Z

batching all the ERC777 token issues together. final call.

#9 - c4-judge

2024-01-31T08:59:05Z

0xean marked the issue as unsatisfactory: Invalid

#10 - c4-judge

2024-01-31T09:00:57Z

0xean removed the grade

#11 - c4-judge

2024-01-31T09:01:07Z

0xean marked the issue as satisfactory

#12 - c4-judge

2024-02-01T11:15:09Z

0xean marked the issue as not a duplicate

#13 - c4-judge

2024-02-01T11:17:19Z

0xean marked the issue as selected for report

#14 - c4-judge

2024-02-01T11:17:29Z

0xean marked the issue as primary issue

#15 - c4-sponsor

2024-02-01T19:32:59Z

Alec1017 (sponsor) confirmed

Awards

1.8029 USDC - $1.80

Labels

bug
2 (Med Risk)
satisfactory
edited-by-warden
duplicate-239

External Links

Lines of code

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

Vulnerability details

Impact

_deriveOrderMetadataHash does not adhere to the eip712 standard by not considering all the encoded values in the orderMetadataTypeString & hence calculates the wrong order metadata hash

Proof of Concept

_deriveRentalTypehashes is responsible for calculating all rental related type hashes for enabling eip 712 signatures

orderMetadataTypeString has the following types that need to be taken into account when creating the orderMetadataTypeHash & OrderMetadataHash

bytes memory orderMetadataTypeString = abi.encodePacked( "OrderMetadata(uint8 orderType,uint256 rentDuration,Hook[] hooks,bytes emittedExtraData)");

but in _deriveOrderMetadataHash, orderType & emittedExtraData aren't used to calculate OrderMetadataHash which voilates the eip 712 specification and hence the wrong hash is calculated

return keccak256( abi.encode( _ORDER_METADATA_TYPEHASH, metadata.rentDuration, keccak256(abi.encodePacked(hookHashes)) ) );

This could be problematic if an off-chain tool uses a particular OrderMetadata struct for hash calculation using the correct EIP712 format thus creating a discrepancy between the reNFT calculated orderMetadataHash and off-chain calculated orderMetadataHash. Hence this could lead to numerous unintended behavior in the future when reNFT protocol expands and used by other off-chain tools.

eip712 clearly states that

The encoding of a struct instance is enc(value₁) β€– enc(valueβ‚‚) β€– … β€– enc(valueβ‚™), i.e. the concatenation of the encoded member values in the order that they appear in the type. Each encoded member value is exactly 32-byte long.

Tools Used

manual review

update _deriveOrderMetadataHash calculation as follows

return keccak256( abi.encode( _ORDER_METADATA_TYPEHASH, metadata.orderType, metadata.rentDuration, keccak256(abi.encodePacked(hookHashes)), bytes32(metadata.emittedExtraData) ) );

Assessed type

Other

#0 - c4-pre-sort

2024-01-21T17:50:03Z

141345 marked the issue as duplicate of #239

#1 - c4-judge

2024-01-28T21:05:19Z

0xean marked the issue as satisfactory

Awards

1.8029 USDC - $1.80

Labels

bug
2 (Med Risk)
satisfactory
duplicate-239

External Links

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L394-L400 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L389-L391

Vulnerability details

Impact

In the Signer._deriveRentalTypehashes function the rentPayloadTypeHash hash is derived as follows:

rentPayloadTypeHash = keccak256( abi.encodePacked( rentPayloadTypeString, orderMetadataTypeString, orderFulfillmentTypeString ) );

The RentPayload struct has two referenced structs namely OrderFulfillment and OrderMetadata.

The EIP712 states the below with respect to referenced structs inside a main struct when it comes to deriving the typeHash.

If the struct type references other struct types (and these in turn reference even more struct types), then the set of referenced struct types is collected, sorted by name and appended to the encoding. An example encoding is Transaction(Person from,Person to,Asset tx)Asset(address token,uint256 amount)Person(address wallet,string name).

As the EIP712 states the reference structs must be sorted by name and appended to the encoding. But in the rentPayloadTypeHash computation, the reference structs seem to be not sorted before concatenating to hash.

Hence the rentPayloadTypeHash typeHash computation does not follow the EIP 712 correctly. This could be problematic if an off-chain tool uses a particular rentPayload struct for hash calculation using the correct EIP712 format thus creating a discrepancy between the reNFT calculated rentPayloadTypeHash and off-chain calculated rentPayloadTypeHash. Hence this could lead to numerous unintended behavior in the future when reNFT protocol expands and used by other off-chain tools.

Proof of Concept

            rentPayloadTypeHash = keccak256(
                abi.encodePacked(
                    rentPayloadTypeString,
                    orderMetadataTypeString,
                    orderFulfillmentTypeString
                )
            );

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

            bytes memory rentPayloadTypeString = abi.encodePacked(
                "RentPayload(OrderFulfillment fulfillment,OrderMetadata metadata,uint256 expiration,address intendedFulfiller)"
            );

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

Tools Used

Manual Review and VSCode

Hence it is recommended to correct the typeHash calculation of the rentPayloadTypeHash according to the EIP712 standard as follows:

rentPayloadTypeHash = keccak256( abi.encodePacked( rentPayloadTypeString, orderFulfillmentTypeString, orderMetadataTypeString ) );

Here the reference struct types are sorted correctly and then appended for encoding and keccak256 hashing.

Assessed type

Other

#0 - c4-pre-sort

2024-01-21T17:50:17Z

141345 marked the issue as duplicate of #239

#1 - c4-judge

2024-01-28T21:04:50Z

0xean marked the issue as satisfactory

Awards

1.8029 USDC - $1.80

Labels

bug
2 (Med Risk)
satisfactory
duplicate-239

External Links

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L406 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L384-L386 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L357-L359

Vulnerability details

Impact

The _ORDER_METADATA_TYPEHASH is calculated in the Signer._deriveRentalTypehashes as shown below:

            bytes memory orderMetadataTypeString = abi.encodePacked(
                "OrderMetadata(uint8 orderType,uint256 rentDuration,Hook[] hooks,bytes emittedExtraData)"
            );

As it is seen the type includes the Hook struct as a reference and it should be concatenated before calculating the typeHash as stated by the EIP712 standard shown below:

If the struct type references other struct types (and these in turn reference even more struct types), then the set of referenced struct types is collected, sorted by name and appended to the encoding.

But above EIP712 definition is not followed while computing the orderMetadataTypeHash as shown below:

            // Derive the OrderMetadata type hash using the corresponding type string.
            orderMetadataTypeHash = keccak256(orderMetadataTypeString);

The referenced struct type (Hook - "Hook(address target,uint256 itemIndex,bytes extraData)") is not appended in the encoding of the orderMetadataTypeHash. Hence the orderMetadataTypeHash does not follow the EIP712 standard while computing the typeHash.

This could be problematic if an off-chain tool uses a particular OrderMetadata struct for hash calculation using the correct EIP712 format thus creating a discrepancy between the reNFT calculated orderMetadataTypeHash and off-chain calculated orderMetadataTypeHash. Hence this could lead to numerous unintended behavior in the future when reNFT protocol expands and used by other off-chain tools.

Proof of Concept

            orderMetadataTypeHash = keccak256(orderMetadataTypeString);

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

            bytes memory orderMetadataTypeString = abi.encodePacked(
                "OrderMetadata(uint8 orderType,uint256 rentDuration,Hook[] hooks,bytes emittedExtraData)"
            );

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

        bytes memory hookTypeString = abi.encodePacked(
            "Hook(address target,uint256 itemIndex,bytes extraData)"
        );

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

Tools Used

Manual Review and VSCode

The orderMetadataTypeHash computation should be corrected to follow the EIP712 standard as shown below:

            orderMetadataTypeHash = keccak256(abi.encodePacked( orderMetadataTypeString, hookTypeString));

Here the hookTypeString which is a referenced struct type is appended to the orderMetadataTypeString thus correctly following the EIP712 standard.

Assessed type

Other

#0 - c4-pre-sort

2024-01-21T17:49:55Z

141345 marked the issue as duplicate of #239

#1 - c4-judge

2024-01-28T21:04:34Z

0xean marked the issue as satisfactory

Awards

1.8029 USDC - $1.80

Labels

2 (Med Risk)
satisfactory
duplicate-239

External Links

Judge has assessed an item in Issue #536 as 2 risk. The relevant finding follows:

L1,L5

#0 - c4-judge

2024-01-28T21:29:01Z

0xean marked the issue as duplicate of #239

#1 - c4-judge

2024-01-28T21:29:05Z

0xean marked the issue as satisfactory

Findings Information

Awards

22.2973 USDC - $22.30

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-65

External Links

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L353 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L90-L100 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L32-L34

Vulnerability details

Impact

The Stop.stopRentBatch function is used to stop a batch of rentals by providing an array of RentalOrder structs to the function.

For each of the RentalOrders in the struct the Stop._reclaimRentedItems function is called as shown below:

            _reclaimRentedItems(orders[i]);

The _reclaimRentedItems function calls the Stop.reclaimRentalOrder function via a delegateCall from the Gnosis safe. In the reclaimRentalOrder function the ERC721 or ERC1155 is transferred to the lender for each of the Items as shown below:

        // Transfer each item if it is a rented asset.
        for (uint256 i = 0; i < itemCount; ++i) { //@audit-info - struct Item { ItemType itemType; SettleTo settleTo; address token; uint256 amount; uint256 identifier; }
            Item memory item = rentalOrder.items[i]; //@audit-info - cache each item

            // Check if the item is an ERC721.
            if (item.itemType == ItemType.ERC721) //@audit-info - enum ItemType { ERC721, ERC1155, ERC20 }
                _transferERC721(item, rentalOrder.lender); //@audit-info - transfer the ERC721 to the lender

            // check if the item is an ERC1155.
            if (item.itemType == ItemType.ERC1155)
                _transferERC1155(item, rentalOrder.lender); //@audit-info - transfer the ERC1155
        }

Let's consider the case of ERC721. Here the _transferERC721 function call is as follows:

    function _transferERC721(Item memory item, address recipient) private {
        IERC721(item.token).safeTransferFrom(address(this), recipient, item.identifier);
    }

The safeTransferFrom is called on the ERC721 contract to transfer the token to the lender. Since the ERC721.safeTransferFrom calls the onERC721Received hook on the lender, a malicious lender can revert this transaction by calling revert inside the onERC721Received hook. This will revert the entire Stop.stopRentBatch batch transaction thus not allowing other lenders to receive their respective ERC721 and ERC1155 tokens.

As a result each of the rentalOrders will have to be stopped by calling them individually via the Stop.stopRent function. Hence there will be delay in lenders getting their NFTs back since each order has to be stopped individually. This could incur monetary loss to the lender since he could have used the NFTs for another purpose (such as gaming) if he received the NFT earlier. And this is not good for the user experience as well. The users might lose the trust on the reNFT if they are unable to use the stopRentBatch feature and having to go through the cumbersome process of having to stop one rentalOrder at a time.

Proof of Concept

            _reclaimRentedItems(orders[i]);

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L353

        for (uint256 i = 0; i < itemCount; ++i) {
            Item memory item = rentalOrder.items[i];

            // Check if the item is an ERC721.
            if (item.itemType == ItemType.ERC721)
                _transferERC721(item, rentalOrder.lender);

            // check if the item is an ERC1155.
            if (item.itemType == ItemType.ERC1155)
                _transferERC1155(item, rentalOrder.lender);
        }

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L90-L100

    function _transferERC721(Item memory item, address recipient) private {
        IERC721(item.token).safeTransferFrom(address(this), recipient, item.identifier);
    }

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L32-L34

Tools Used

Manual Review and VSCode

Hence it is recommended to execute the _reclaimRentedItems(orders[i]) function call inside a try-catch block and handle the single rentalOrder reverts via a fallback appropriately. This will ensure a single malicious lender is unable to deprive other lenders getting their ERC721 and ERC1155 tokens on time and also unable to deprive renters getting their ERC20 tokens (In the event of a PAY order) when the Stop.stopRentBatch is called.

Assessed type

DoS

#0 - c4-pre-sort

2024-01-21T18:02:26Z

141345 marked the issue as duplicate of #65

#1 - c4-judge

2024-01-28T19:22:57Z

0xean marked the issue as satisfactory

#2 - c4-judge

2024-01-28T20:52:00Z

0xean changed the severity to 3 (High Risk)

#3 - c4-judge

2024-01-30T14:21:44Z

0xean changed the severity to 2 (Med Risk)

Awards

12.582 USDC - $12.58

Labels

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

External Links

L-01 Missing salt paramter in EIP 712 eip712DomainTypehash calculation

Lines of code

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

Impact

EIP 712 specifies that a salt param can be used for the domain type has calculation to fully adhere to the specification whereas in this case it is not the case

Tools Used

manual review

Consider salt when calculating eip712DomainTypehash

L-02 hook item index can be out of bounds with the offer & rental item array

Lines of code

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

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L218

Impact

There is no check that the item index in the hook array is valid when adding or removing hooks which might result in tx revert when creating/stopping a rental

Tools Used

manual review

add a validation check to make sure that itemIndex <= offer.length/rental.length

L-03 No check for contract bytecode size during create2 deployment

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Create2Deployer.sol#L54

Impact

deploying a contract without any bytecode will be a waste of execution overhead & gas

Tools Used

manual review

add a check if(iszero(extcodesize(deploymentAddress))) revert();

L-04 Avoid reacasting the Module instance again

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L514

Impact

The getModuleForKeycode mapping returns the module instance after casting so we can avoid re-casting to save gas

Tools Used

manual review

remove re-casting of the module instance

L-05 Use abi.encodePacked for computing eip 712 typehashes

Lines of code

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

Impact

To completely adhere to the EIP 712 standard abi.encodePacked should be used instead of abi.encode when comoputing rentalOrderTypeHash

Tools Used

manual review

use abi.encodePacked instead of abi.encode when comoputing rentalOrderTypeHash

L-06 Remove extra hook validation check in guard policy

Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L338

Impact

STORE.hookOnTransaction returns whether a hook is valid or not but there is an extra check(hook != address(0)) to determine hook validity when a tx is triggered which is not necessary and costs extra gas

Tools Used

manual review

update the if statement to if(isActive)

#0 - 141345

2024-01-22T13:56:24Z

#1 - c4-judge

2024-01-27T20:31:20Z

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