xanoutas/solidity-security-findings huggingface dataset:
All orders can be hijacked to lock rental assets forever by tipping a malicious 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
The `Create` contract is responsible for creating a rental. It achieves this by acting as a Seaport `Zone`, and storing and validating orders as rentals when they are fulfilled on Seaport.

However, one thing it doesn't account for is the fact that Seaport allows for "tipping" in the form of ERC20 tokens as part of the order fulfillment process. This is done by extending the `consideration` array in the order with additional ERC20 tokens.

From the Seaport [docs](https://docs.opensea.io/reference/seaport-overview#order) (emphasis mine):
> The `consideration` contains an array of items that must be received in order to fulfill the order. It contains all of the same components as an offered item, and additionally includes a `recipient` that will receive each item. This array **may be extended by the fulfiller on order fulfillment so as to support "tipping"** (e.g. relayer or referral payments).

This other passage, while discussing a different issue, even highlights the root cause of this vulnerability (the zone does not properly allocate consideration extensions):
 > As extensions to the consideration array on fulfillment (i.e. "tipping") can be arbitrarily set by the caller, fulfillments where all matched orders have already been signed for or validated can be frontrun on submission, with the frontrunner modifying any tips. Therefore, it is important that orders fulfilled in this manner either leverage "restricted" order types with a **zone that enforces appropriate allocation of consideration extensions**, or that each offer item is fully spent and each consideration item is appropriately declared on order creation.

Let's dive in and see how tipping works exactly. We know fulfillers may use the entry points listed [here](https://github.com/code-423n4/2024-01-renft/blob/75e7b44af9482b760aa4da59bc776929d1e022b0/docs/fulfilling-a-rental.md#overview), the first of which is simply a wrapper to [`_validateAndFulfillAdvancedOrder()`](https://github.com/re-nft/seaport-core/blob/3bccb8e1da43cbd9925e97cf59cb17c25d1eaf95/src/lib/OrderFulfiller.sol#L78C14-L78C46). This function calls [`_validateOrderAndUpdateStatus()`](https://github.com/re-nft/seaport-core/blob/3bccb8e1da43cbd9925e97cf59cb17c25d1eaf95/src/lib/OrderValidator.sol#L135) , which derives the order hash by calling [`_assertConsiderationLengthAndGetOrderHash()`](https://github.com/ProjectOpenSea/seaport-core/blob/main/src/lib/Assertions.sol#L69). At the end of the trail, we can see that the order hash is finally derived in [`_deriveOrderHash()`](https://github.com/ProjectOpenSea/seaport-core/blob/main/src/lib/GettersAndDerivers.sol#L64) from other order parameters as well as the consideration array, but [only up to](https://github.com/ProjectOpenSea/seaport-core/blob/main/src/lib/GettersAndDerivers.sol#L142)  the `totalOriginal
Attacker can rug all listed NFTs
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

An attacker knowing how to use the `create2` opcode could pre-deploy a Safe contract a specific address, call the `setApprovalForAll` function of any NFT contracts listed, destroy the Safe through delegatecall and then deploy a new Safe at the same address using the protocol directly (making it a legit Safe).
This would then let the attacker rug any NFTs contract that he approved For All previously (see Proof Of Concept).

## Proof of Concept

As per the [Factory.sol](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Factory.sol) contract, the `deployRentalSafe(...)` function is making a call the the [`SafeProxyFactory`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Factory.sol#L180-L186).
This function is opened to anyone (see [createProxyWithNonce](https://github.com/safe-global/safe-contracts/blob/b140318af6581e499506b11128a892e3f7a52aeb/contracts/proxies/SafeProxyFactory.sol#L56-L61)).
It uses the `CREATE2` opcode to deploy a new contract. This opcode allows to deploy a contract at a specific address defined by the `salt` passed to it.

In the case of the reNFT protocol, when calling this factory, the `salt` is computed the following way:

```solidity
                uint256(keccak256(abi.encode(STORE.totalSafes() + 1, block.chainid)))
```

Since I can know what is the `STORE.totalSafes()` and the `block.chainid` I can compute the `salt` and deploy a contract at the same address before.

I could:

- call directly the SafeFactory and deploy a Safe contract at an address that will soon be used by the protocol and passing the initialisation data i want since it does not have any impact on the address calculation. For the sake of the example, let's say I will deploy a Safe that will have the same salt as the one that will be deployed by the protocol when `STORE.totalSafes()` is equal to 10
- call from the Safe the [`setApprovalForAll`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L124) function on one of the NFT contract that is listed on the protocol. Giving approval for all to another address I own (let's say `address(a)`)
- destroy the Safe through a delegatecall
- wait that the `STORE.totalSafes()` is 9 and call the [`deployRentalSafe(...)`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Factory.sol#L138-L194) function of the protocol so that my Safe is deployed at the same address as the one I pre-deployed and destroyed previously
- I can now take a rental that involves the NFT contract that I set approval for all previously
- use the `transferFrom` function of the NFT contract from `address(a)` to transfer the NFT to another address I own

## Tools Used
Missing Revert Parameter in stopRent Function
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

If there are no hooks to process, the current code does not explicitly handle this scenario. Without a revert , the condition might not be working as intended.

## Proof of Concept
there is apotential issue in the stopRent function of the stop.sol contract. In the specific section where hooks are processed:
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L348C1-L348C1

  if (order.hooks.length > 0) {
    _removeHooks(order.hooks, order.items, order.rentalWallet);
  }
It appears that there is no revert parameter if the logic order.hooks.length is equal to or less than 0. This means the function might proceed without handling this case.
## Tools Used

## Recommended Mitigation Steps

 Add revert param after if 


## Assessed type

Other
An attacker can hijack any ERC1155 token he rents due to a design issue in reNFT via reentrancy exploitation
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

### Pre-requisite knowledge & an overview of the features in question
---

1. **Gnosis safe fallback handlers**: Safes starting with version 1.1.1 allow to specify a fallback handler. A gnosis safe fallback handler is a contract which handles all functions that is unknown to the safe, this feature is meant to provide a great flexibility for the safe user. The safe in particular says "If I see something unknown, then I just let the fallback handler deal with it."

    **Example**: If you want to take a uniswap flash loan using your gnosis safe, you'll have to create a fallback handler contract with the callback function `uniswapV2Call()`. When you decide to take a flash loan using your safe, you'll send a call to `swap()` in the uniswap contract. The uniswap contract will then reach out to your safe contract asking to call `uniswapV2Call()`, but `uniswapV2Call()` isn't actually implemented in the safe contract itself, so your safe will reach out to the fallback handler you created, set as the safe's fallback handler and ask it to handle the `uniswapV2Call()` TX coming from uniswap.

    **Setting a fallback handler**: To set a fallback handler for your safe, you'll have to call the function [`setFallbackHandler()`](https://github.com/safe-global/safe-contracts/blob/b140318af6581e499506b11128a892e3f7a52aeb/contracts/base/FallbackManager.sol#L44) which you can find it's logic in [FallbackManager.sol](https://github.com/safe-global/safe-contracts/blob/main/contracts/base/FallbackManager.sol)


---
### The Vulnerability

---
In order to make sense of the vulnerability, we need to understand the token transferral & rental registeration execution flow first.

**Step 1**: First of all, before the fulfillment process begins, both the lender and the borrower need to approve the Seaport Conduit to spend their tokens on behalf of them. The lender approves the conduit to spend the NFT token which he wants to lend (offer item) and the borrower approves the ERC20 tokens he will use as a payment method for this rental (consideration item).

**Step 2**: Once the fulfillment process begins, the conduit begins the token transferral process. The conduit transfers the lender's NFT tokens to the borrower's gnosis rental safe, then it transfers the borrower's ERC20 tokens to the Payment Escrow.

*Note 1: Keep in mind that the rental is not registered yet.*.

*Note 2: The Seaport Conduit utilizes the `safeTransferFrom` function to transfer the ERC1155 tokens which will trigger `onERC1155Receive` hook on the receiver of the ERC1155 tokens, in this case, it's the borrower's rental safe. However, when it comes to the transferral of ERC721 tokens, it uses `transferFrom` and not `safeTransferFrom`*.

**Step 3**: Once the tokens are transferred, Seaport will communicate with the Zone contract. You can think of the zon
When stopping a rental malicious user can steal tokens from another renter's rental safe
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Before presenting the issue I will give some background information on the stopping of a rental. When stopping a rental only one parameter is supplied to the `stopRent` function: `RentalOrder calldata order`. From this parameter, the same `orderHash`, supplied on rental creation, needs to be derived for `stopRent` to be called successfully. This is the data stored in an order's unique hash:
```
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
    )
);
```
One thing that it does not contain is the `order.rentalWallet`, which is where the problem arises.

Let's follow the code of `stopRent` to see where and how `order.rentalWallet` is used:
1/ Firstly it is used to get the `rentalId` of a rental item, which is then stored in the `rentalAssetUpdates` bytes array:
```
for (uint256 i; i < order.items.length; ++i) {
    if (order.items[i].isRental()) {
        // Insert the rental asset update into the dynamic array.
        _insert(
            rentalAssetUpdates,
            order.items[i].toRentalId(order.rentalWallet),
            order.items[i].amount
        );
    }
}
```
From this, a malicious actor can point to any rental item from any pending rental that has the same identifier and token address as the item that we are currently looping through. This is the data stored inside each `rentalId`: `keccak256(abi.encodePacked(recipient, token, identifier))`. 
Therefore, by only changing the recipient, which is the `order.rentalWallet`, to another rental wallet that also contains an amount of the same token, the malicious actor has gained access to a valid rental item that is not connected to the rental order they are stopping.

2/ After that, the `order.rentalWallet` is included in a call to `_removeHooks`. We can ignore this as many orders will not be making use of hooks and this will be skipped.

3/ Following that, `reclaimRentedItems` is called, where a call to the `order.rentalWallet` is made, which then calls `reclaimRentalOrder`. The rental wallet then sends the rented tokens to the order lender. The major issue is that as the rental wallet can be set to any wallet that has a balance of the rented tokens, the lender would be receiving assets that do not belong to them but to the victim's rental wallet. To summarise: rented tokens are sent not the lender that rented them out but to the lender of the order that is currently getting stopped, due to the wrong rental wallet that was supplied to the function initially.

4/ `settlePayment` is called which does not interact with the rental wallet so it is irrelevant to
Rental safes can call Guard.checkTransaction directly to execute hooks with arbitrary params
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When Guard.checkTransaction is executed, onTransaction hooks may be executed according to the data of the transaction. It's possible, however, for rental safes to call checkTransaction directly with arbitrary data to execute hooks with arbitrary parameters.

**Note**: This finding is unique from the following findings:
- "Possible to call Guard.checkTransaction directly with arbitrary data to fire onTransaction hook"
- "It's possible to get onTransaction hooks to fire without actually executing the underlying function"

As stated in the docs: "Findings are duplicates if they share the same root cause. More specifically, if fixing the Root Cause (in a reasonable manner) would cause the finding to no longer be exploitable, then the findings are duplicates."

These findings all have different root causes and mitigations and therefore are not duplicates.

## Proof of Concept
When checkTransaction is called, we check whether the to address has a corresponding hook and has the onTransaction hook enabled, and if so we execute hook.onTransaction with the parameters to be used on the function which we're checking.

```solidity
// Fetch the hook to interact with for this transaction.
address hook = STORE.contractToHook(to);
bool isActive = STORE.hookOnTransaction(hook);

// If a hook exists and is enabled, forward the control flow to the hook.
if (hook != address(0) && isActive) {
    _forwardToHook(hook, msg.sender, to, value, data);
}
```

checkTransaction is called by Safe.execTransaction to validate the transaction before execution, blocking certain calls. However, calling checkTransaction itself is not blocked. As a result, it's possible to call checkTransaction directly, with any arbitrary data and it will be passed to the hook as if that data were to be executed by the Safe. This can change state in the hook contracts in unexpected ways.

## Tools Used
- Manual review

## Recommended Mitigation Steps
Prevent calls from the safe to Guard.checkTransaction, either by blocking all calls to the Guard contract or simply blocking the checkTransaction selector.





## Assessed type

Invalid Validation
Lack of Contract Verification in updateHookPath Function
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/Storage.sol#L294-L303


# Vulnerability details

The updateHookPath function in the smart contract Storage.sol lacks proper verification of whether the provided address (to) is a contract. This poses a critical security risk as the address is subsequently used in a DelegateCall operation. It is crucial to add a check to ensure that the specified address is indeed a contract before assigning it to the _contractToHook mapping.

## Impact
This vulnerability could potentially lead to unexpected behavior or exploitation if a non-contract address is provided to the updateHookPath function. An attacker might be able to manipulate the contract's state or perform malicious actions by providing an address that is not a contract.

## Proof of Concept
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/Storage.sol#L294-L303
## Tools Used

## Recommended Mitigation Steps
```solidity
// Import OpenZeppelin's Address library
import "@openzeppelin/contracts/utils/Address.sol";

// ...

function updateHookPath(address to, address hook) external onlyByProxy permissioned {
    // Ensure that the `to` address is a contract.
    require(Address.isContract(to), Errors.StorageModule_NotContract(to));

    // Ensure that the `hook` address is a contract.
    require(Address.isContract(hook), Errors.StorageModule_NotContract(hook));

    // Point the `to` address to the `hook` address.
    _contractToHook[to] = hook;
}
```


## Assessed type

Access Control
Incorrect `gnosis_safe_disable_module_offset` constant leads to removing the rental safe's `module` without verification
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/libraries/RentalConstants.sol#L58
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/libraries/RentalConstants.sol#L62
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/interfaces/ISafe.sol#L98-L101
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L255
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L260
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol#L266
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/test/unit/Guard/CheckTransaction.t.sol#L369
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/test/unit/Guard/CheckTransaction.t.sol#L557


# Vulnerability details

The `gnosis_safe_disable_module_offset` constant was incorrectly specified to point at an incorrect function parameter of the `disableModule(address prevModule, address module)`.

Specifically, the offset constant will point at the `prevModule` (1st param) instead of the `module` (2nd param).

## Impact

When a safe transaction initiated from a rental safe containing a call to the safe's `disableModule()` is invoked, the `Guard::checkTransaction()` cannot verify the `module` expected to be removed.

If the `prevModule` was a non-whitelisted extension, the safe transaction will be reverted. 

However, if the `prevModule` was a whitelisted extension, the `module` will be removed without verification. Removing the rental safe's `module` without verification can lead to other issues or attacks since the removed `module` can be a critical component (e.g., removing the protocol's `Stop` policy contract).

## Proof of Concept

The snippet below presents some of the Gnosis Safe configurations of the `reNFT protocol`. The [`gnosis_safe_disable_module_selector`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/libraries/RentalConstants.sol#L58) constant contains the function selector (`0xe009cfde`) of the rental safe's `ModuleManager::disableModule(address prevModule, address module)`.

Meanwhile, the [`gnosis_safe_disable_module_offset`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/libraries/RentalConstants.sol#L62) constant contains a memory offset (`0x24`) intended to point at the `module` param of the `disableModule()`.

```solidity
    // FILE: https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/libraries/RentalConstants.sol

    /////////////////////////////////////////////////////////////////////////////////
    //                  Gnosis Safe Function Selectors And Offsets                 //
    ///////////////
reNFT Protocol is not EIP712 compliant (See coded POC)
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## IMPACT

Protocol is not EIP712 compliant, which will result in issues with integrators. (See Coded POC for proof)

## VULNERABILITY DETAILS

When implementing `EIP712`, among other things, for data structures that will be a part of signing message, a `typehash` must be defined. The structure `typehash`  https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct is defined as 

`typeHash = keccak256(encodeType(typeOf(s)))`

Where `encodeType` https://eips.ethereum.org/EIPS/eip-712#definition-of-encodetype  is defined as `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` where each member is written as `type ‖ " " ‖ name`

---

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.

*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)`

The protocol uses several structs like 

```solidity
struct RentPayload {
    OrderFulfillment fulfillment; 
    OrderMetadata metadata; 
    uint256 expiration;
    address intendedFulfiller;
}
```

and

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

in the protocol, the precalculated typehash for each of the structure are of different structures and evaluated in the `Signer::_deriveRentalTypehashes()`function

- for `rentalOrderTypeHash`,

```solidity
// Construct the Item type string.
        bytes memory itemTypeString = abi.encodePacked(
            "Item(uint8 itemType,uint8 settleTo,address token,uint256 amount,uint256 identifier)"
        );

        // Construct the Hook type string.
        bytes memory hookTypeString = abi.encodePacked(
            "Hook(address target,uint256 itemIndex,bytes extraData)"
        );

        bytes memory rentalOrderTypeString = abi.encodePacked(
            "RentalOrder(bytes32 seaportOrderHash,Item[] items,Hook[] hooks,uint8 orderType,address lender,address renter,address rentalWallet,uint256 startTimestamp,uint256 endTimestamp)"
        );

        // Derive the Item type hash using the corresponding type string.
        itemTypeHa