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: 73/116
Findings: 3
Award: $30.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: JCN
Also found by: 0xDING99YA, 0xpiken, ABAIKUNANBAEV, AkshaySrivastav, Audinarey, Aymen0909, DanielArmstrong, J4X, Krace, KupiaSec, Qkite, Ward, evmboi32, fnanni, hash, juancito, kaden, krikolkk, ravikiranweb3, rbserver, rvierdiiev, serial-coder, trachev, zach, zzzitron
15.9479 USDC - $15.95
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#L166-L183
Stop::stopRent() function can be front run by a bad actor to block the user from claiming their deposited asset back. The assets could be locked permanently in the safe.
In the RentalOrder structure, "rentalWallet" field is not used for creating the rental order hash. This leaves an opportunity for a bad actor to pass a different rentalWallet value while calling the stopRent() ahead of the valid user by taking priority with higher gas.
The front runner can pass the below sub calls successfully in the stopRent() function.
a) _validateRentalCanBeStoped() b) removehooks() c) rentalAssetUpdates value update d) ESCRW.settlePayment(order); e) STORE.removeRentals( _deriveRentalOrderHash(order), _convertToStatic(rentalAssetUpdates) )
The only problem function is _reclaimRentedItems(). If the front running caller can by pass the _reclaimRentedItems(), function, he should be able to execute the attack successfully.
So to hack, the front running caller passes an attack contract that implements a dummy "execTransactionFromModule()" function and returns true. This will ensure that attacker will by pass the asset claiming process and will proceed to settlePayment() against the rentals and also remove the records for assets rented in the protocol from the storage.
This front running execution will succeed.
Subsequently, when the transaction of the real valid user is processed, it will revert as there are no assets for the rental order hash in the storage.[They were already deleted by the front run transaction]
Hence the user will never be able to claim their assets back and will loose the rented assets permanently.
Manual Review
a) include the rentalWallet as part of rental order hash. b) if there are limitations, then map the rentalWallet to rental Order hash and keep it in mapping so that claiming can be done on the rentalWallet only.
This will prevent such bypass and hence prevent locking of rented assets permanently.
Timing
#0 - c4-pre-sort
2024-01-21T19:27:02Z
141345 marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-21T19:27:22Z
141345 marked the issue as duplicate of #239
#2 - c4-judge
2024-01-28T20:18:20Z
0xean changed the severity to 2 (Med Risk)
#3 - c4-judge
2024-01-28T21:06:06Z
0xean marked the issue as satisfactory
#4 - c4-judge
2024-01-30T11:03:59Z
0xean marked the issue as not a duplicate
#5 - c4-judge
2024-01-30T11:04:08Z
0xean marked the issue as duplicate of #385
#6 - c4-judge
2024-01-30T14:24:45Z
0xean changed the severity to 3 (High Risk)
🌟 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
Signer::_deriveRentalOrderHash() function is relaying on the caller to pass the Order data in exact same order, else the order data would result in different Rental Order hash. This may create working problems later on.
Lets say a rent Order is being created as below at the time of creating the order.
// Create the items Item[] memory items = new Item[](2); items[0] = Item({ itemType: ItemType.ERC721, settleTo: SettleTo.LENDER, token: address(erc721s[0]), amount: 1, identifier: 0 }); items[1] = Item({ itemType: ItemType.ERC721, settleTo: SettleTo.LENDER, token: address(erc721s[1]), amount: 1, identifier: 0 }); // Create a mock rental order RentalOrder memory order = RentalOrder({ seaportOrderHash: keccak256(abi.encode("seaportOrderHash")), items: items, hooks: new Hook[](0), orderType: OrderType.BASE, lender: bob.addr, renter: alice.addr, rentalWallet: address(alice.safe), startTimestamp: 10, endTimestamp: 11 });
But, when the stop::stopRent() function is called, the order of the items are ordered as below.
Item[] memory items = new Item[](2); items[0] = Item({ itemType: ItemType.ERC721, settleTo: SettleTo.LENDER, @audit->token: address(erc721s[1]), amount: 1, identifier: 0 }); items[1] = Item({ itemType: ItemType.ERC721, settleTo: SettleTo.LENDER, @audit->token: address(erc721s[0]), amount: 1, identifier: 0 });
While the underlying data is exactly same, since the elements are ordered differently in Items array, the resulting RentalOrderHash will be very different.
This will prevent the stopRent() function from executing the order until the caller know the exact order and submits the stopRent() in the same order as it was used during the creation of order.
This will function like DOS until the order is fixed.
Manual Review
Implement a sorting order for Items array and hooks array so that irrespective of how the caller passes the order of items, the data is rearranged on chain so that as long as underlying data is same, the RentalOrderHash generate is same and relieves the caller from responsibility of order the underlying data in certain order every time for function to work properly.
Other
#0 - c4-pre-sort
2024-01-21T17:55:26Z
141345 marked the issue as duplicate of #239
#1 - c4-judge
2024-01-28T21:06:06Z
0xean marked the issue as satisfactory
#2 - c4-pre-sort
2024-02-02T09:35:17Z
141345 marked the issue as not a duplicate
#3 - c4-judge
2024-02-02T11:18:11Z
0xean marked the issue as duplicate of #239
#4 - c4-judge
2024-02-02T11:25:46Z
0xean marked the issue as not a duplicate
#5 - c4-judge
2024-02-02T11:27:34Z
0xean marked the issue as duplicate of #418
#6 - c4-judge
2024-02-02T11:27:38Z
0xean marked the issue as partial-25
#7 - c4-judge
2024-02-02T11:34:09Z
0xean marked the issue as not a duplicate
#8 - c4-judge
2024-02-02T11:34:20Z
0xean marked the issue as duplicate of #239
#9 - c4-judge
2024-02-02T11:34:33Z
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
Reference: https://www.zaryabs.com/push0-opcode/
Although PUSH0 opcode is now included with the latest solidity compiler, you need to be careful while using it on any other chain than ETH mainnet. There are still other chains like L2s that don't recognize the PUSH0 opcode. Therefore, if you try to use the latest compiler to deploy your contract on any such chain that doesn't support this opcode, your contract deployment shall fail.```
#0 - 141345
2024-01-22T13:57:40Z
48 ravikiranweb3 l r nc 1 0 2
L 1 n L 2 l L 3 i L 4 n
#1 - c4-judge
2024-01-27T20:31:20Z
0xean marked the issue as grade-b