xanoutas/solidity-security-findings huggingface dataset:
Malicious actor can steal any actively rented NFT and freeze the rental payments (of the affected rentals) in the `escrow` contract
Audit: 2024-01-renft-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Bug Description
The main exploit presented in this report takes advantage of multiple bugs. Some of these bugs on their own can be viewed as having their own "lesser" impacts. However, this report can be considered as my demonstration of the `greatest impact` for all the bugs described below:

1. The `stopRent`/`stopRentBatch` functions do not validate the existence of the supplied `RentalOrder` until after the lender is sent the specified NFT. Therefore, a non-existent `RentalOrder` can be supplied and then created during the callback to the lender (`onERC721Received/onERC1155Received`). (**Successful exploitation of this bug is dependent on the other bugs below**)
2. The signature digest signed by the protocol signer is predictable and not necessarily unique to a specific order. Therefore, a generic signature can be retrieved from theoretically any order (via the front-end) and can then be used arbitrarily (for multiple fulfillments) until `metadata.expiration > block.timestamp`. This enables the above bug to be utilized, as a required signature is able to be retrieved beforehand so that the order creation and fulfillment process can be executed atomically for multiple orders. (**Successful exploitation of this bug is not dependent on the other bugs mentioned**)
3. Several core functions in `Signer.sol` are not compliant with EIP-712. This can be leveraged with the above bugs to obtain a valid signature and use this signature with multiple orders that have different `metadata.orderType` values. This also allows arbitrary `safe wallet` addresses to be included in the `RentalOrder` struct supplied to the `stop` functions. (**Successful exploitation of this bug is not dependent on the other bugs mentioned**) 

Combined, the above vulnerabilities allow the theft and freezing of any and/or all NFTs currently being rented out (active or expired, as long as the rental has not been stopped). The rental payments for the affected rentals will also be frozen in the `escrow` contract. The outline of this `Bug Description` section will be as follows:

1. The main exploit path will be discussed
2. Prerequisites for the main exploit will be discussed
3. Lesser, independently achievable, impacts of the independent vulnerabilities (2 & 3) will be briefly explained

### Main exploit path
In order to thoroughly detail this exploit path I will provide my explanation as a means to prove that the following actions are possible:

- A non existent `RentalOrder` can be passed into a `stop` function and pass validation. The only requirement for this exploit is that the `RentalOrder.orderType` specifies a `PAY` order and `msg.sender == RentalOrder.lender`.
- The only fields in this `Rental
Blacklisted USDC Borrower Can Retain NFT Indefinitely
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
When a borrower is blacklisted by USDC and the lender sets ERC20 as USDC, transferring USDC to the borrower becomes impossible, leading to a chain of reverts:

    PaymentEscrow._settlePaymentInFull reverts
    PaymentEscrow._settlePayment reverts
    PaymentEscrow.settlePayment reverts
    Stop.stopRent reverts

As a result, the borrower's PAY rentals can't be stopped, allowing them to keep the NFT indefinitely.

## Proof of Concept
The implementation of USDC's transfer and transferFrom functions differs from other ERC20 contracts. It first checks if the account is on the blacklist.


```js
    modifier notBlacklisted(address _account) {
        require(
            !blacklisted[_account],
            "Blacklistable: account is blacklisted"
        );
        _;
    }
    /**
     * @notice Transfer tokens from the caller
     * @param to    Payee's address
     * @param value Transfer amount
     * @return True if successful
     */
    function transfer(address to, uint256 value)
        external
        override
        whenNotPaused
        notBlacklisted(msg.sender)
        notBlacklisted(to)
        returns (bool)
    {
        _transfer(msg.sender, to, value);
        return true;
    }

    /**
     * @notice Transfer tokens by spending allowance
     * @param from  Payer's address
     * @param to    Payee's address
     * @param value Transfer amount
     * @return True if successful
     */
    function transferFrom(
        address from,
        address to,
        uint256 value
    )
        external
        override
        whenNotPaused
        notBlacklisted(msg.sender)
        notBlacklisted(from)
        notBlacklisted(to)
        returns (bool)
    {
        require(
            value <= allowed[from][msg.sender],
            "ERC20: transfer amount exceeds allowance"
        );
        _transfer(from, to, value);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
        return true;
    }

```

1. The blacklisted borrower borrows an NFT 
2. The rental process can't be halted due to fail of usdc transcations. 


## Tools Used
Foundry

## Recommended Mitigation Steps

Allow users to claim their fees instead of sending them directly to the user's address.


## Assessed type

ERC20
Transfers may fail due to zero value amounts in pro-rata payment calculation, preventing rentals from being stopped
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L159-L179
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L132-L146
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100-L118


# Vulnerability details

## Impact
The `PaymentEscrow` contract is responsible for escrowing rental payments while rentals are active. When rentals are stopped, this module determines payouts to all parties. For `PAY` orders that are stopped before the rental period has ended, the [`_settlePaymentProRata()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L159) function in this contract calculates the pro-rata payment for the renter and lender and transfers the respective amounts. 

However, there is a potential issue with the implementation of this function. It first calls the internal function [`_calculatePaymentProRata()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L132) to calculate the renter and lender amounts. Due to the precision loss in the calculation, there can be scenarios where either the `renterAmount` or the `lenderAmount` becomes zero, both close to the beginning and the end of the total duration of the rental.

The problem arises when these zero amounts are passed on to the [`_safeTransfer()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100)function. Some ERC20 tokens revert when attempting to transfer 0 tokens. If such a token is used as payment in a `PAY` order, the transaction will revert at certain periods, preventing the renter or lender from stopping the rental. In an (unrealistic) worst-case scenario, this locks the funds until the rental period is over.

## Proof of Concept
Let's consider the extreme case of a `PAY` order with `amount = 1` and `totalTime = 1000`. The calculation of `renterAmount` in the original code is as follows:


$$
\begin{aligned}
\text{numerator} &= (\text{amount} \times \text{elapsedTime}) \times 1000, \\
\text{renterAmount} &= \left(\frac{\text{numerator}}{\text{totalTime}} + 500\right) / 1000.
\end{aligned}
$$

Substituting `amount = 1` and `totalTime = 1000`, we get:

$$
\begin{aligned}
\text{numerator} &= (1 \times \text{elapsedTime}) \times 1000, \\
\text{renterAmount} &= \left(\frac{\text{elapsedTime} \times 1000}{1000} + 500\right) / 1000 \\
                   &= \frac{\text{elapsedTime} + 500}{1000}.
\end{aligned}
$$


For `elapsedTime < 500`, the `renterAmount` becomes less than 1, as the addition of `500` and division by `1000` results in a value less than 1. Since `renterAmount` must be an integer, it will be rounded down to 0.

Conversely, for `elapsedTime ≥ 500`, `renterAmou
Renters can extend rental duration and hold lender's tokens hostage if ERC-777 is used as payment in a `PAY` order
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100-L119


# Vulnerability details

## Impact
The `PaymentEscrow` contract serves as an escrow for rental payments while rentals are active and handles their transfer when they are stopped. `PAY` orders can be stopped by the lender at any time, in which case the payment is split pro rata among the lender and the renter, and by anybody once the rental has expired, in which case the payment goes in full to the renter.

However, if an ERC-777 token is used as payment in a `PAY` order and the renter is a contract address, the renter can extend the duration of the rental until they receive the rental in full by reverting on the `tokensReceived()` hook until the full duration is over. This will cause the call to `transfer()` in the [`_safeTransfer()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100) function to revert and prevent the lender from stopping the rental:
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L100-L119

```solidity
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)
	);
	/*...*/
	if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
		revert Errors.PaymentEscrowModule_PaymentTransferFailed(token, to, value);
	}
}
```

The renter can be a contract as, per the sponsor:
> Since the renter is the one doing the fulfilling and calling seaport, and the renter will be the offerer of the PAYEE order, it means that the renter does not have to sign this order. it can just be passed in.

And anyway:
> Seaport supports signing orders with smart contracts via eip-1271

Furthermore, the renter could potentially extort the lender for larger amounts, as they can hold the NFTs hostage for as long as they want at no cost.

## Proof of Concept
1. Alice (the lender) creates a PAY order with an ERC-777 token as payment.
2. Bob (the renter), a contract address, fulfills the order.
3. Bob reverts on the `tokensReceived()` hook, extending the duration of the rental.
4. Alice is unable to stop the rental until the full duration is over.
5. Bob can hold the NFTs hostage, potentially extorting Alice for larger amounts.

## Tools Used
Manual review

## Recommended Mitigation Steps
One possible mitigation would be to implement a mechanism to allow the renter to recuperate their items once a `PAY` rental has ended even if the transfer to the renter fails. Alternatively, an allowance could be set and execution resumed if a transfer fails, so that the recipient can pull the transfer themselves.


## Assessed type

ERC20
If the fulfiller is USDC blacklisted and the ERC20 payment is in USDC, the offerer cannot get their NFT back
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
If the fulfiller is USDC blacklisted and the ERC20 payment is in USDC, the offerer cannot get their NFT back

## Proof of Concept
In Stop.sol, `stopRent()` will handle transfer both ERC20 payment and assets(ERC721/ERC1155) when a rental order expires. 

The vulnerability is that when the fulfiller is USDC blacklisted and if the ERC20 token in question is USDC, the offerer will not abe able to get their asset token back, due to the fact that `ECRW.settlePayment(order)` which handles ERC20 payment will revert during token transfer, and reverting the entire `stopRent()` transaction.

```solidity
//src/policies/Stop.sol
    function stopRent(RentalOrder calldata order) external {
...
|>       ESCRW.settlePayment(order);
...
```
(https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L296)


## Tools Used

Manual Review

## Recommended Mitigation Steps

Consider adding try-catch black inside the ESCRW.settlePayment() token transfer flow when the erc20 is USDC or other ERC20 with blacklisting features.





## Assessed type

Other
Renter can burn ERC721Burnable & ERC1155Burnable tokens
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The [`Guard`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Guard.sol) policy acts as a Gnosis Safe guard designed to protect rented assets from unauthorized transfers. It does this by checking the target and function selector of each transaction initiated by a rental safe and reverting transactions that could potentially lead to unauthorized transfers. However, the current implementation of the `Guard.sol` contract does not protect against the burning of [`ERC721Burnable`](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721Burnable) and [`ERC1155Burnable`](https://docs.openzeppelin.com/contracts/5.x/api/token/erc1155#ERC1155Burnable) tokens.

`ERC721Burnable` and `ERC1155Burnable` are widely adopted standards that define a `burn(tokenId)` and `burn(account, id, value)`/`burnBatch(account, ids, values)` functions, respectively. If a lender rents out an NFT from a collection that implements one of these standards, the renter will be able to burn the lender's token.

Here are a few examples of collections with large market capitalization that implement one of these standards:

- TinFun [0xa177dbcca6b180fb80336e062ab4dc8cd7e53693](https://etherscan.io/address/0xa177dbcca6b180fb80336e062ab4dc8cd7e53693)
- PudgyPenguins [0xBd3531dA5CF5857e7CfAA92426877b022e612cf8](https://etherscan.io/address/0xBd3531dA5CF5857e7CfAA92426877b022e612cf8)
- OVERWORLD INCARNA [0xfdf5acd92840e796955736b1bb9cc832740744ba](https://etherscan.io/address/0xfdf5acd92840e796955736b1bb9cc832740744ba)
- LilPudgys [0x524cab2ec69124574082676e6f654a18df49a048](https://etherscan.io/address/0x524cab2ec69124574082676e6f654a18df49a048)

Given the prevalence of these standards and the ease with which this issue can be exploited, it is classified as high severity.
## Proof of Concept
Consider the following scenario:

1. Alice owns an NFT from the TinFun collection and decides to rent it out using the reNFT protocol.
2. Bob rents Alice's NFT.
3. Bob calls the `burn(tokenId)` function of the TinFun contract, passing in the ID of Alice's NFT.
4. The `Guard.sol` contract does not prevent this action, and Alice's NFT is burned.

## Tools Used
Manual review

## Recommended Mitigation Steps
To mitigate this issue, the `Guard.sol` contract should be updated to block the `burn()` and `burnBatch()` functions of `ERC721Burnable` and `ERC1155Burnable` tokens. This could be achieved by adding additional checks in the `_checkTransaction()` function.

However, it is also worth pointing out that some tokens might implement non-standard transfer or approval granting functions, which also wouldn't be blocked by the guard. Since users may not be aware of the existence of these functions, it is likely that this will lead to loss of funds sooner or later.

Another poten
encodedData argument of OrderMetadata hashStruct is missing struct members which is not EIP712 compliant 
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The encoding of OrderMetadata `encodedData` is inaccurately performed, leading to issues in verifying EIP712 signed messages.

`Signer::_deriveOrderMetadataHash()` is used in `Create::_isValidOrderMetadata()` and `Signer::_deriveOrderMetadataHash()` to check if the hash of the `OrderMetadata` struct is valid. However, there's a mistake in the encoding process because it doesn't hash all the members of **`OrderMetadata`**.

```solidity
function _deriveOrderMetadataHash(
    OrderMetadata memory metadata
) internal view returns (bytes32) {
    // Create array for hooks.
    bytes32[] memory hookHashes = new bytes32[](metadata.hooks.length);

    // Iterate over each hook.
    for (uint256 i = 0; i < metadata.hooks.length; ++i) {
        // Hash the hook
        hookHashes[i] = _deriveHookHash(metadata.hooks[i]);
    }

    // Derive and return the metadata hash as specified by EIP-712.
    // @audit why not pass 'orderType' and 'emittedExtraData'
@>  return
        keccak256(
            abi.encode(
                _ORDER_METADATA_TYPEHASH,
                metadata.rentDuration,
                keccak256(abi.encodePacked(hookHashes))
            )
        );
}
```

As you can see the `encodedData` includes only `OrderMetadata.rentDuration` and `OrderMetadata.hooks`, while `OrderMetadata.orderType` and `OrderMetadata.emittedExtraData` are not included.

According to EIP712, all struct members should be hashed in the order that they are defined.

> 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.
> 

Read More [here](https://eips.ethereum.org/EIPS/eip-712#definition-of-encodedata)

All of this will result in that only wrong hashed OrderMetadata though Signer::_deriveOrderMetadataHash() to pass when Seaport call validateOrder(). If the Lender pass correctly composed hash in `Seaport::OrderComponents::zoneHash` by himself and do not use `Create::getOrderMetadataHash()` for it, any rental will revert because in `Create::_isValidOrderMetadata()`.
This situation leads to an issue where only an incorrectly hashed `OrderMetadata` can pass through `Signer::_deriveOrderMetadataHash()` during the Seaport `validateOrder()`. Even if the Lender provides the correct hash in `Seaport::OrderComponents::zoneHash`, any rental will still revert in `Create::_isValidOrderMetadata()`.

## Proof of Concept

In the tests, when the lender provides the `zoneHash`, it relies on the incorrect `_deriveOrderMetadataHash()`. As a result, all these tests will pass, even though the underlying issue exists.

For accurate testing, use the correct OrderMetadata hash. This hash is generated in Remix using the fixed function outlined in
Rented assets will be locked in safe forever if lender or renter is blacklisted by consideration token
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The `Stop` policy provides functionality to stop a rental, which involves transferring the rented items back to the lender and the tokens in escrow to the lender, renter or both based on the type of order and point in time. This is implemented in the [`stopRent()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L265) and [`stopRentBatch()`](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L313) functions, which require both the transfers of the rented items and of the tokens in escrow to succeed:
```solidity
function stopRent(RentalOrder calldata order) external {
	...
	
	// Interaction: Transfer rentals from the renter back to lender.
	_reclaimRentedItems(order);

	// Interaction: Transfer ERC20 payments from the escrow contract to the respective recipients.
	ESCRW.settlePayment(order);
	...
```

However, there is a potential issue if the lender's address is unable to receive a consideration ERC20, for example, due to being blacklisted. In such a case, when a `BASE` rental is stopped, the lender will not be able to recover the rented assets either as the transaction will revert.

The same applies for the renter in case of a PAY transaction, in which case the lender won't be able to recover the rented assets either.

This could potentially lead to a situation where rented assets are locked forever in the safe.

## Proof of Concept
Consider the following scenario:

1. Alice (the lender) creates a `PAY` order to rent out her NFTs and provides USDC as a consideration item.
2. Bob fulfills the order and the NFTs are transferred to the rental safe while the USDC is held in escrow.
3. Bob gets blacklisted in the USDC contract, possibly on purpose.
4. Alice tries to stop the rental using the `stopRent()` function in the `Stop.sol` contract.
5. The `stopRent()` function attempts to transfer the rented NFTs back to Alice and the USDC tokens in escrow, or part of them, to Bob.
6. However, because Bob is blacklisted, the transfer of the USDC tokens to his address fails.
7. As a result, the entire `stopRent()` transaction reverts, and Alice is unable to recover her rented NFTs.

## Tools Used
Manual review

## Recommended Mitigation Steps
A possible solution to this issue could be to allow the lender to forfeit the payment if they are the one calling `stopRent()`. This would ensure that even if any actor is blacklisted and cannot receive the consideration ERC20 token, they can still recover their rented assets.



## Assessed type

ERC20