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: 13/116
Findings: 4
Award: $1,050.34
π Selected for report: 1
π Solo Findings: 0
π Selected for report: CipherSleuths
Also found by: BARW
1013.6566 USDC - $1,013.66
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
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
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.
manual review
It is recommended to prohibit erc777 tokens from being used as consideration items.
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
π 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
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
_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
_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.
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) ) );
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
π 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
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
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.
rentPayloadTypeHash = keccak256( abi.encodePacked( rentPayloadTypeString, orderMetadataTypeString, orderFulfillmentTypeString ) );
bytes memory rentPayloadTypeString = abi.encodePacked( "RentPayload(OrderFulfillment fulfillment,OrderMetadata metadata,uint256 expiration,address intendedFulfiller)" );
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
.
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
π 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
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
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.
orderMetadataTypeHash = keccak256(orderMetadataTypeString);
bytes memory orderMetadataTypeString = abi.encodePacked( "OrderMetadata(uint8 orderType,uint256 rentDuration,Hook[] hooks,bytes emittedExtraData)" );
bytes memory hookTypeString = abi.encodePacked( "Hook(address target,uint256 itemIndex,bytes extraData)" );
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.
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
π 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 #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
π Selected for report: stackachu
Also found by: 0xA5DF, 0xDING99YA, 0xc695, CipherSleuths, EV_om, HSP, cccz, evmboi32, hals, hash, jasonxiale, juancito, kaden, lanrebayode77, rbserver
22.2973 USDC - $22.30
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
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.
_reclaimRentedItems(orders[i]);
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); }
function _transferERC721(Item memory item, address recipient) private { IERC721(item.token).safeTransferFrom(address(this), recipient, item.identifier); }
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.
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)
π 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
eip712DomainTypehash
calculationEIP 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
manual review
Consider salt
when calculating eip712DomainTypehash
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
manual review
add a validation check to make sure that itemIndex <= offer.length/rental.length
deploying a contract without any bytecode will be a waste of execution overhead & gas
manual review
add a check if(iszero(extcodesize(deploymentAddress))) revert();
Module
instance againThe getModuleForKeycode
mapping returns the module instance after casting so we can avoid re-casting to save gas
manual review
remove re-casting of the module instance
abi.encodePacked
for computing eip 712 typehashesTo completely adhere to the EIP 712 standard abi.encodePacked
should be used instead of abi.encode
when comoputing rentalOrderTypeHash
manual review
use abi.encodePacked
instead of abi.encode
when comoputing rentalOrderTypeHash
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
manual review
update the if statement to if(isActive)
#0 - 141345
2024-01-22T13:56:24Z
536 CipherSleuths l r nc 1 0 3
L 1 d dup of https://github.com/code-423n4/2024-01-renft-findings/issues/239 L 2 l L 3 n L 4 n L 5 d dup of https://github.com/code-423n4/2024-01-renft-findings/issues/239 L 6 n
#1 - c4-judge
2024-01-27T20:31:20Z
0xean marked the issue as grade-b