xanoutas/solidity-security-findings huggingface dataset:
`Signer:_deriveOrderMetadataHash`: malformed orders can be fulfilled due to incorrect hashing
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/packages/Signer.sol#L218-L240
https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/policies/Create.sol#L626-L640
https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/policies/Create.sol#L411


# Vulnerability details

## Impact

If an user makes mistake and put out incorrect order, it can still be fulfilled by swapping the OrderType.


## Proof of Concept

The full proof of concept can be found in [this gist](https://gist.github.com/zzzitron/e65f8371d650f25afe81e158a3fd92bc)

Below is the relevant code snippet:

In this proof of concept, an user is mistakenly put out Base order, with erc20 as the offer instead of consideration.
This order should not be able to be fulfilled as the OrderType and the offering/consideration does not match.
However, the OrderMetadata's OrderType is not included in the hash for the signature of the Seaport order.
Therefore the Bob can simply pick up this order and swap the orderType as PAY order and fulfill it as PAY order.
It would be unfavorable for Alice, since she meant to put out Base order.

```solidity
    function test_Success_Rent_BaseOrderMistake() public {
        // alice creates a base order
        // but mistakenly puts erc20 as the offer instead of consideration
        // Nobody should be able to fulfill this order due to the mismatch of the OrderType and the offering
        createOrder({
            offerer: alice,
            orderType: OrderType.BASE,
            erc721Offers: 1,
            erc1155Offers: 0,
            erc20Offers: 1,
            erc721Considerations: 0,
            erc1155Considerations: 0,
            erc20Considerations: 0
        });

        // finalize the pay order creation
        (
            Order memory payOrder,
            bytes32 payOrderHash,
            OrderMetadata memory payOrderMetadata
        ) = finalizeOrder();

        // create a PAYEE order. The fulfiller will be the offerer.
        createOrder({
            offerer: bob,
            orderType: OrderType.PAYEE,
            erc721Offers: 0,
            erc1155Offers: 0,
            erc20Offers: 0,
            erc721Considerations: 1,
            erc1155Considerations: 0,
            erc20Considerations: 1
        });

        // finalize the pay order creation
        (
            Order memory payeeOrder,
            bytes32 payeeOrderHash,
            OrderMetadata memory payeeOrderMetadata
        ) = finalizeOrder();

        // Before Bob fulfills the order, bob just exchange the orderType to PAY order
        payOrderMetadata.orderType = OrderType.PAY;

        // create an order fulfillment for the pay order
        createOrderFulfillment({
            _fulfiller: bob,
            order: payOrder,
            orderHash: payOrderHash,
            metadata: payOrderMetadata
        });

        // create an order
Signer: missing rentalWallet in the `_deriveRentalOrderHash` enables improper transfer of assets or locking the asset
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/packages/Signer.sol#L181-L194
https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/policies/Stop.sol#L299-L302


# Vulnerability details

## Impact

Users may abuse this exploitation to transfer out rental property before the end of the rents.
Or, block the original owner to reclaim the rental property by locking the asset in the safe.
There might be even more attack vectors.

## Proof of Concept

The full proof of concept based on the `BaseTest` can be found in [this gist](https://gist.github.com/zzzitron/e65f8371d650f25afe81e158a3fd92bc)

The following is the relevant code snippet.

In the code below, it is demonstrated to transfer out the rental property before the rent period is over.
The attacker controls alice, bob and carol's accounts. While the dan is the victim.
The attacker sends the same erc1155 with id 0 to the alice and carol's address.
The attacker creates a base order as alice and fulfills as bob. Note that the rent period is 500.
Then the attacker creates another base order with the same token erc1155s[0], id 0, but makes the rent period 1000.
Dan will fulfill the order, thinking he can use the NFT for the period of 1000.
However, the attacker can transfer out the asset only after the alice's order is ended, which is 500 in this case.

She could achieve this, by simply put's the dan's safe as the rentalWallet for the first order.

However, it would essentially lock the first order's asset in the safe.
(Due to the fact that the order is already removed)
In this case does not matter, since the attacker also controls the bob's safe.

This attack vector can be used to lock an innocent offerer's asset in the safe. 


```solidity
    function test_StopRent_BaseOrder_usingDifferntSafe() public {
        // alice and carol has 100 of erc1155s[0]
        erc1155s[0].mint(alice.addr, 200);
        vm.prank(alice.addr);
        erc1155s[0].safeTransferFrom(address(alice.addr), address(carol.addr), 0, 100, "");

        // 1. alice creates a base order for erc1155s[0] and bob fulfills it
        // create a BASE order
        orderToCreate.offerer = alice;
        orderToCreate.offerItems.push(
          OfferItemLib.empty()
            .withItemType(ItemType.ERC1155)
            .withToken(address(erc1155s[0]))
            .withIdentifierOrCriteria(0)
            .withStartAmount(100)
            .withEndAmount(100)
        );
        orderToCreate.considerationItems.push(
          ConsiderationItemLib.empty()
            .withRecipient(address(ESCRW))
            .withItemType(ItemType.ERC20)
            .withToken(address(erc20s[0]))
            .withStartAmount(100)
            .withEndAmount(100)
        );
        orderToCreate.metadata.orderType = OrderType.BASE;
        orderToCreate.metadata.rentDuration = 500;
        orderToCreate.metadata.emittedExtraData = new bytes(0);
        // fi
Hook tokens allow renter's to gain access to the rented asset before the hook has been setup with `onStart`
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

Bypass if the NFT is transferred before hook token

## Impact
Rental asset can be used before it's hook have been setup with onStart method in case of ERC777 tokens

## Proof of Concept
Some hooks may be configured by invoking the `onStart` method inside the `validateOrder` callback from Seaport

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L495-L503
```solidity
            try
                IHook(target).onStart(
                    rentalWallet,
                    offer.token,
                    offer.identifier,
                    offer.amount,
                    hooks[i].extraData
                )
``` 

When filling a restricted order(orders with zones) in seaport:
1. The zone is called after the associated assets have been transferred
2. The offer items are transferred before the consideration items[docs](https://docs.opensea.io/reference/seaport-overview#fulfill-order)
 
This means that the rentalWallet will be having the rental asset before the consideration token is transferred from the fulfiller.
ERC777 tokens allow callbacks to be setup when tokens are transferred from an account. This allows an attacker to use the rented asset before it has configured correctly.

### POC Test
https://gist.github.com/10xhash/da97ccf3ac90a130a879c1a7004c3521

## Tools Used
Manual Review

## Recommended Mitigation Steps
Inform about this issue for hook tokens or always configure hooks in such a manner that `onStart` provides more leniency while default mode is more restrictive. 


## Assessed type

Token-Transfer
Missing rentalWallet field in orderHash allows stealing from other rental wallets
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Attacker can steal rental assets from other rental wallets

## Proof of Concept
The stored rental order hash doesn't contain the rental wallet.

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L181-L194
```solidity
    function _deriveRentalOrderHash(
        RentalOrder memory order
    ) internal view returns (bytes32) {
        
        .....

        /*
        
        struct RentalOrder {
    bytes32 seaportOrderHash;
    Item[] items;
    Hook[] hooks;
    OrderType orderType;
    address lender;
    address renter;
    address rentalWallet;
    uint256 startTimestamp;
    uint256 endTimestamp;
        }
        
        */

        // @audit missing order.rentalWallet
        return
            keccak256(
                abi.encode(
                    _RENTAL_ORDER_TYPEHASH,
                    order.seaportOrderHash,
                    keccak256(abi.encodePacked(itemHashes)),
                    keccak256(abi.encodePacked(hookHashes)),
                    order.orderType,
                    order.lender,
                    order.renter,
                    order.startTimestamp,
                    order.endTimestamp
                )
            );
    }
```

Since an ERC1155 token can be present in multiple rental wallets, this makes it possible for an attacker to withdraw the rented token from a different rental wallet when stopping a rental order. 

### POC Test
Apply the following diff and run `forge test --mt testHash_StealFromOtherRentalWallet`

Withdrawing from another rental wallet test
```diff
diff --git a/test/integration/StopRent.t.sol b/test/integration/StopRent.t.sol
index 3d19d3c..3f646f3 100644
--- a/test/integration/StopRent.t.sol
+++ b/test/integration/StopRent.t.sol
@@ -6,6 +6,8 @@ import {Order} from "@seaport-types/lib/ConsiderationStructs.sol";
 import {OrderType, OrderMetadata, RentalOrder} from "@src/libraries/RentalStructs.sol";
 
 import {BaseTest} from "@test/BaseTest.sol";
+import {Errors} from "@src/libraries/Errors.sol";
+import {console} from "forge-std/Test.sol";
 
 contract TestStopRent is BaseTest {
     function test_StopRent_BaseOrder() public {
@@ -65,6 +67,97 @@ contract TestStopRent is BaseTest {
         assertEq(erc20s[0].balanceOf(address(ESCRW)), uint256(0));
     }
 
+        function testHash_StealFromOtherRentalWallet() public {
+        // alice lends erc1155
+        createOrder({
+            offerer: alice,
+            orderType: OrderType.BASE,
+            erc721Offers: 0,
+            erc1155Offers: 1,
+            erc20Offers: 0,
+            erc721Considerations: 0,
+            erc1155Considerations: 0,
+            erc20Considerations: 1
+        });
+
+        // finalize the order creation
+        (
+            Order memory orderAliceBob,
Missing access control in Factory::initializeRentalSafe
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Factory.sol#L122-L128


# Vulnerability details

## Impact
In the `initializeRentalSafe()` is missing access control, it could potentially lead to unauthorized parties modifying the curves state variable. This lack of access control could be exploited by attackers to manipulate the behavior of the contract or disrupt its intended functionality.

## Proof of Concept
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Factory.sol#L122-L128

## Tools Used
Code review

## Recommended Mitigation Steps
use `kernel` modifier


## Assessed type

Access Control
The lender of the payee order may loss their ERC20.
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
PAY order have at least one ERC721/ERC1155, at least one ERC20.
These orders are paired with PAYEE orders.
PAYEE order have at least one ERC721/ERC1155, at least one ERC20.
In Create.sol, when utilizing validateOrder(), the execution of _rentFromZone initiates a rental order.
```
function validateOrder(
        ZoneParameters calldata zoneParams
    ) external override onlyRole("SEAPORT") returns (bytes4 validOrderMagicValue) {
        /* ... */
        // Create a payload of seaport data.
        SeaportPayload memory seaportPayload = SeaportPayload({
            orderHash: zoneParams.orderHash,
            zoneHash: zoneParams.zoneHash,
            offer: zoneParams.offer,
            consideration: zoneParams.consideration,
            totalExecutions: zoneParams.totalExecutions,
            fulfiller: zoneParams.fulfiller,
            offerer: zoneParams.offerer
        });

        // Check: The signature from the protocol signer has not expired.
        _validateProtocolSignatureExpiration(payload.expiration);

        // Check: The fulfiller is the intended fulfiller.
        _validateFulfiller(payload.intendedFulfiller, seaportPayload.fulfiller);

        // Recover the signer from the payload.
        address signer = _recoverSignerFromPayload(
            _deriveRentPayloadHash(payload),
            signature
        );

        // Check: The data matches the signature and that the protocol signer is the one that signed.
        if (!kernel.hasRole(signer, toRole("CREATE_SIGNER"))) {
            revert Errors.CreatePolicy_UnauthorizedCreatePolicySigner();
        }

        // Initiate the rental using the rental manager.
        _rentFromZone(payload, seaportPayload);
/* ... */
    }
```
Create a PAYEE order, where the ERC721 is identical to the PAY order.
And set the recipient to be the same as the offerer. 
Can pass _executionInvariantChecks.
```
function _executionInvariantChecks(
        ReceivedItem[] memory executions,
        address expectedRentalSafe
    ) internal view {
        for (uint256 i = 0; i < executions.length; ++i) {
            ReceivedItem memory execution = executions[i];

            // ERC20 invariant where the recipient must be the payment escrow.
            if (execution.isERC20()) {
                _checkExpectedRecipient(execution, address(ESCRW));
            }
            // ERC721 and ERC1155 invariants where the recipient must
            // be the expected rental safe.
            else if (execution.isRental()) {
                _checkExpectedRecipient(execution, expectedRentalSafe);
            }
            // Revert if unsupported item type.
            else {
                revert Errors.CreatePolicy_SeaportItemTypeNotSupported(
                    execution.itemType
                );
            }
        }
    }
```

In Orde
The lender of the payee can borrow the NFT without any ERC20.
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
PAY order have at least one ERC721/ERC1155, at least one ERC20.
These orders are paired with PAYEE orders.
PAYEE order have at least one ERC721/ERC1155, at least one ERC20.
In Create.sol, when utilizing validateOrder(), the execution of _rentFromZone initiates a rental order. 
```
function validateOrder(
        ZoneParameters calldata zoneParams
    ) external override onlyRole("SEAPORT") returns (bytes4 validOrderMagicValue) {
        /* ... */
        // Create a payload of seaport data.
        SeaportPayload memory seaportPayload = SeaportPayload({
            orderHash: zoneParams.orderHash,
            zoneHash: zoneParams.zoneHash,
            offer: zoneParams.offer,
            consideration: zoneParams.consideration,
            totalExecutions: zoneParams.totalExecutions,
            fulfiller: zoneParams.fulfiller,
            offerer: zoneParams.offerer
        });

        // Check: The signature from the protocol signer has not expired.
        _validateProtocolSignatureExpiration(payload.expiration);

        // Check: The fulfiller is the intended fulfiller.
        _validateFulfiller(payload.intendedFulfiller, seaportPayload.fulfiller);

        // Recover the signer from the payload.
        address signer = _recoverSignerFromPayload(
            _deriveRentPayloadHash(payload),
            signature
        );

        // Check: The data matches the signature and that the protocol signer is the one that signed.
        if (!kernel.hasRole(signer, toRole("CREATE_SIGNER"))) {
            revert Errors.CreatePolicy_UnauthorizedCreatePolicySigner();
        }

        // Initiate the rental using the rental manager.
        _rentFromZone(payload, seaportPayload);
/* ... */
    }
```
Create a PAYEE order, where the ERC20 is identical to the PAY order.
And set the recipient to be the same as the offerer.
Can pass _executionInvariantChecks.
```
function _executionInvariantChecks(
        ReceivedItem[] memory executions,
        address expectedRentalSafe
    ) internal view {
        for (uint256 i = 0; i < executions.length; ++i) {
            ReceivedItem memory execution = executions[i];

            // ERC20 invariant where the recipient must be the payment escrow.
            if (execution.isERC20()) {
                _checkExpectedRecipient(execution, address(ESCRW));
            }
            // ERC721 and ERC1155 invariants where the recipient must
            // be the expected rental safe.
            else if (execution.isRental()) {
                _checkExpectedRecipient(execution, expectedRentalSafe);
            }
            // Revert if unsupported item type.
            else {
                revert Errors.CreatePolicy_SeaportItemTypeNotSupported(
                    execution.itemType
                );
            }
        }
    }
```
In OrderC
attacker can match base, pay and payee order at the same time, as a result, escrow's accounting will be wrong and it can be drained
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
If the order is Base or Pay, ESCRW will increase the deposit. 
```
// PAYEE orders are considered mirror-images of a PAY order. So, PAYEE orders
        // do not need to be processed in the same way that other order types do.
        if (
            payload.metadata.orderType.isBaseOrder() ||
            payload.metadata.orderType.isPayOrder()
        ) {
            /* .... */

            // Interaction: Update storage only if the order is a Base Order or Pay order.
            STORE.addRentals(orderHash, _convertToStatic(rentalAssetUpdates));

            // Interaction: Increase the deposit value on the payment escrow so
            // it knows how many tokens were sent to it.
            for (uint256 i = 0; i < items.length; ++i) {
                if (items[i].isERC20()) {
                    ESCRW.increaseDeposit(items[i].token, items[i].amount);
                }
            }
```
The way [Seaport matches orders](https://docs.opensea.io/reference/seaport-overview) is as follows:
Calling one of two "match" functions, matchOrders and matchAdvancedOrders, where a group of explicit orders are supplied alongside a group of fulfillments specifying which offer items to apply to which consideration items。 Note that orders fulfilled in this manner do not have an explicit fulfiller; instead, Seaport will simply ensure coincidence of wants across each order.
Consider the following scenario: 
| |Base | Pay| Payee 1 | Payee 2 |
| --------|-------- | -------- | -------- | --------|
|Offer items |Nft A     | NFT B and 15 ERC20     | none   |none |
|Consideration items |10 ERC20     | none    | Nft A and 2 ERC20     |Nft B and 3 ERC20|

The PAY order rented from the BASE order and corresponds with two PAYEE orders.
BASE order increases  10 in escrow.
Pay order increases  15 in escrow.
In this case, PAY only gives 15 to escrow.
The escrow's accounting will be incorrect.
This would cause a loss for the escrow, and in the worst-case scenario, it could lead to the escrow being completely depleted. 


## Proof of Concept
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L601 

## Tools Used

## Recommended Mitigation Steps


## Assessed type

Context
Incorrect implementation of EIP712: missing values and wrong encoding
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L182-L194
_deriveOrderMetadataHash](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L232-L238
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L151
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L299-L302
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L293


# Vulnerability details

## Impact

An incorrect implementation of EIP712 not necessarily introduces risks. In this case there is one element in particular that can be used to break the protocol: order.rentalWallet is missing from the Rental Order hash derivation. For this reason, a corrupt rentalWallet value can be used to stop a rental, reclaiming items from a different safe than it is supposed to. In some cases the attacked rental safe can recover the assets once the rental finishes, but in other cases the assets could get lost.

## Proof of Concept

#### Missing values

In the [_deriveRentalOrderHash()](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L182-L194) function, the encoding is missing order.rentalWallet (between `order.renter` and `order.startTimestamp`).

In the [_deriveOrderMetadataHash()](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L232-L238) function, the encoding is missing metadata.orderType and metadata.emittedExtraData.

#### Wrong encoding in HookHash

[EIP712 spec](https://eips.ethereum.org/EIPS/eip-712#definition-of-encodedata) states that "The dynamic values bytes and string are encoded as a keccak256 hash of their contents". However, in the [_deriveHookHash()](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Signer.sol#L151) function `extraData` is encoded directly. `extraData` is of type bytes and therefore should be encoded as a keccak256 hash.

#### Cross rental safe attack

Let's see how an attacker can take advantage of `order.rentalWallet` being missing from the Rental Order hash derivation. 

During rental creation this doesn't seem to be a problem, since `order.rentalWallet` is taken from `zoneParams.fulfillment.recipient`, which is safe. We should keep in mind though that the bugged orderHash is used to store the rental asset updates (See [Create.sol L595](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Create.sol#L595)).

On the other hand, when the rent is stopped, a RentalOrder with a different rentalWallet can be used as input. The conditions for a rentalWallet to work are the following:
1. It should be an active rental safe with Stop.sol enabled