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: 45/116
Findings: 4
Award: $103.81
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: sin1st3r__
Also found by: 0xA5DF, J4X, JCN, Jorgect, KupiaSec, evmboi32, jasonxiale, kaden, said
67.1301 USDC - $67.13
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L235-L243 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L126-L136 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/Storage.sol#L118-L128
Guard._checkTransaction
is used to prevent transactions that involve transferring an ERC721 or ERC1155. In current implementation, if the renter borrows an ERC721/ERC1155 token, Guard._checkTransaction
makes sure that the token can't be transferred.
The issue is that ERC1155, one token ID may represent more than a NFT token, in such case. If a renter already owns some ERC1155 for tokenId
, and he borrows some tokenId
from the system, then until the rential expires, he can't transfer his own ERC1155.
In Guard._checkTransaction is called when rent safe wants to transfer some token, within Guard._checkTransaction
, Guard._revertSelectorOnActiveRental is used to check ERC1155
function _revertSelectorOnActiveRental( bytes4 selector, address safe, address token, uint256 tokenId ) private view { // Check if the selector is allowed. if (STORE.isRentedOut(safe, token, tokenId)) { revert Errors.GuardPolicy_UnauthorizedSelector(selector); } }
And STORE.isRentedOut is defined as:
function isRentedOut( address recipient, address token, uint256 identifier ) external view returns (bool) { // calculate the rental ID RentalId rentalId = RentalUtils.getItemPointer(recipient, token, identifier); // Determine if there is a positive amount return rentedAssets[rentalId] != 0; }
So it means that if rentedAssets[rentalId] is larger than 0, the transfer for the token will be revert, this is Ok for ERC721, because for ERC721, one tokenId means one NFT, but for ERC1155, this will prevent the renter from transferring his own ERC1155 token
Suppose Alice has some ERC1155 for 0x1234 tokenId, Bob list his 0x1234 token on the market and Alice borrows Bob's token. Until Alice repay Bob's rential order, Alice can't transfer her own ERC1155.
VIM
Other
#0 - c4-pre-sort
2024-01-21T18:00:13Z
141345 marked the issue as duplicate of #600
#1 - c4-judge
2024-01-27T18:09:33Z
0xean changed the severity to 3 (High Risk)
#2 - c4-judge
2024-01-28T11:19:38Z
0xean changed the severity to 2 (Med Risk)
#3 - c4-judge
2024-01-28T11:20:35Z
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#L218-L239 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L339-L409 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L180-L194
Signer._deriveRentalTypehashes
and Signer._deriveOrderMetadataHash
don't follow EIP-712
struct OrderMetadata { // Type of order being created. OrderType orderType; // Duration of the rental in seconds. uint256 rentDuration; // Hooks that will act as middleware for the items in the order. Hook[] hooks; // Any extra data to be emitted upon order fulfillment. bytes emittedExtraData; }
But when calculating hash in Signer.sol#L231-L238, orderType
and emittedExtraData
members are missing
218 function _deriveOrderMetadataHash( 219 OrderMetadata memory metadata 220 ) internal view returns (bytes32) { ... 229 230 // Derive and return the metadata hash as specified by EIP-712. 231 return 232 keccak256( 233 abi.encode( 234 _ORDER_METADATA_TYPEHASH, 235 metadata.rentDuration, 236 keccak256(abi.encodePacked(hookHashes)) 237 ) 238 ); 239 }
rentPayloadTypeHash
, the order of param of abi.encodePacked
is abi.encodePacked(rentPayloadTypeString, orderMetadataTypeString, orderFulfillmentTypeString), this is incorrect.
Quoting from EIP-712If 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).
So the order should be sorted by name, but the code doesn't sort the struct by names
339 function _deriveRentalTypehashes() 340 internal 341 pure 342 returns ( 343 bytes32 itemTypeHash, 344 bytes32 hookTypeHash, 345 bytes32 rentalOrderTypeHash, 346 bytes32 orderFulfillmentTypeHash, 347 bytes32 orderMetadataTypeHash, 348 bytes32 rentPayloadTypeHash 349 ) 350 { ... 387 388 // Construct the RentPayload type string. 389 bytes memory rentPayloadTypeString = abi.encodePacked( 390 "RentPayload(OrderFulfillment fulfillment,OrderMetadata metadata,uint256 expiration,address intendedFulfiller)" 391 ); 392 393 // Derive RentPayload type hash via combination of relevant type strings. 394 rentPayloadTypeHash = keccak256( 395 abi.encodePacked( <<<---- Here the order of abi.encodePacked is not correct 396 rentPayloadTypeString, 397 orderMetadataTypeString, 398 orderFulfillmentTypeString 399 ) 400 ); 401 ... 407 } 408 }
VIM
Other
#0 - c4-pre-sort
2024-01-21T17:50:34Z
141345 marked the issue as duplicate of #239
#1 - c4-pre-sort
2024-01-21T17:50:36Z
141345 marked the issue as duplicate of #239
#2 - c4-judge
2024-01-28T21:05:14Z
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#L265-L306 https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L313-L364
Stop.stopRent
and Stop.stopRentBatch
are used to terminated a rental, and in this function ERC721/ERC1155 will be returned to lender's address, and the payment(ERC20) will be sent to lender/renter depending on the rental type.
If the renter's safe or lender's safe is balcklisted for the borrowed ERC721/ERC1155 or payment ERC20, Stop.stopRent/stopRentBatch
will be reverted, in such case, the borrowed assets will be stuck in rental's safe.
I'll take Stop.stopRent
as an exampel:
In Stop.stopRent, ESCRW.settlePayment is used to transfer ERC20 payments from the escrow contract to the respective recipients.
In PaymentEscrow._settlePayment will finally calls PaymentEscrow._safeTransfer to transfer the token PaymentEscrow._safeTransfer is defined as:
function _safeTransfer(address token, address to, uint256 value) internal { // Call transfer() on the token. (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transfer.selector, to, value) ); // Because both reverting and returning false are allowed by the ERC20 standard // to indicate a failed transfer, we must handle both cases. // // If success is false, the ERC20 contract reverted. // // If success is true, we must check if return data was provided. If no return // data is provided, then no revert occurred. But, if return data is provided, // then it must be decoded into a bool which will indicate the success of the // transfer. if (!success || (data.length != 0 && !abi.decode(data, (bool)))) { revert Errors.PaymentEscrowModule_PaymentTransferFailed(token, to, value); } }
In PaymentEscrow._safeTransfer
, transfer
is used to send token to recipients.
If ERC20 tokens(like USDC with blacklist) are used in the order, when from
or to
in in blacklist, the transfer
tx will be reverted. In such case, Stop.stopRent
will be reverted.
PAY orders
, in the rental order, she submit a ERC721 and a ERC20, the ERC20 is USDC( or other ERC20 with blacklist).Stop.stopRent
to get her ERC721 back, during ESCRW.settlePayment(order);, the function will be reverted in PaymentEscrow._safeTransfer because Bob's address is blacklisted.VIM
Other
#0 - c4-pre-sort
2024-01-21T18:02:12Z
141345 marked the issue as duplicate of #65
#1 - c4-judge
2024-01-28T19:25:22Z
0xean marked the issue as satisfactory
#2 - c4-judge
2024-01-28T20:51:59Z
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
Create._processPayOrderOffer
and Create._processPayeeOrderConsideration
can be bypassed partiallyFile:
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L276-L286
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L218-L220
In _processBaseOrderOffer
only check offers.length shoulde be larger than 0, if the offer type is ERC1155, the function doesn't check if ERC1155's amount is larger than 0. If the offers in Create._processBaseOrderOffer
are all ERC115 type, and 0 as amount, it acts like offers.length == 0, and should revert
Similar issue happens in Create._processPayOrderOffer too.
Stop.stopRent
or stop.stopRentBatch
is not called, after the rental duration, the renter can continue use the lender's token for freeFile:
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L313-L365
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L265-L306
In current implementation, after the rential duration, the ERC721/ERC1155 will be owned by the renter if Stop.stopRent/stopRentBatch
is not called, this might be an issue because the renter can use the assets for free
#0 - 141345
2024-01-22T13:57:26Z
484 jasonxiale l r nc 2 0 0
L 1 l L 2 l
#1 - c4-judge
2024-01-27T20:31:16Z
0xean marked the issue as grade-b