xanoutas/solidity-security-findings huggingface dataset:
Only executor can change executor in Kernel
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L277-L302


# Vulnerability details

## Impact

As discussed with the sponsors `executor` is a critical role that will be given when `Kernel` is being deployed. Then the only way in which the executor can be changed is through `executeAction` which has `onlyExecutor` modifier. 

[Kernel.sol#L252-L257](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L251C3-L257C6)

```solidity
/**
     * @dev Modifier which only allows calls by an executing address.
     */
    modifier onlyExecutor() {
        if (msg.sender != executor) revert Errors.Kernel_OnlyExecutor(msg.sender);
        _;
    }
```

[Kernel.sol#L277-L302](https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L277-L302)

```solidity
/**
     * @dev Executes an action on a target address.
     *
     * @param action_ Action which will be performed.
     * @param target_ Address upon which the action will operate.
     */
    function executeAction(Actions action_, address target_) external onlyExecutor {
        if (action_ == Actions.InstallModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _installModule(Module(target_));
        } else if (action_ == Actions.UpgradeModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _upgradeModule(Module(target_));
        } else if (action_ == Actions.ActivatePolicy) {
            ensureContract(target_);
            _activatePolicy(Policy(target_));
        } else if (action_ == Actions.DeactivatePolicy) {
            ensureContract(target_);
            _deactivatePolicy(Policy(target_));
        } else if (action_ == Actions.MigrateKernel) {
            ensureContract(target_);
            _migrateKernel(Kernel(target_));
        } else if (action_ == Actions.ChangeExecutor) {
            executor = target_;
        } else if (action_ == Actions.ChangeAdmin) {
            admin = target_;
        }

        emit Events.ActionExecuted(action_, target_);
    }
```

This can result in catastrophic consequences if the executor turns out to be malicious or if his private key is stolen. 

In our discussion it was mentioned that the `executor` won’t be a multisig wallet, but a normal **EOA**.

> A similar problem can be found in the C4 Moonwell audit:
https://github.com/code-423n4/2023-07-moonwell-findings/issues/315
> 

## Proof of Concept

There are various actions that can be performed in order to harm the protocol and it’s users:

- Executor can upgrade modules, for example `PaymentEscrow`, and wipe out the internal balance which is used to keep track of the `ERC20` tokens that are sent when order is fulfilled and should be settled to the users after rent is stopped. 
That will lead to the inability to te
Modules and Policies remain active in old Kernel mappings after migration
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L508-L517


# Vulnerability details

## Impact

When migrating a Kernel, the state of Modules and Policies are updated to reflect the new Kernel address but the old Kernel still keeps them in its mappings. This causes the old Kernel contract to still show the modules and policies as existing and although the Kernel will be bricked, this is not visible from its state.

## Proof of Concept

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L508-L517

```solidity
function _migrateKernel(Kernel newKernel_) internal {
        uint256 keycodeLen = allKeycodes.length;

        // For each keycode stored in the kernel.
        for (uint256 i; i < keycodeLen; ++i) {
            // get the module represented by the keycode.
            Module module = Module(getModuleForKeycode[allKeycodes[i]]);
            // Instruct the module to change the kernel.
            module.changeKernel(newKernel_);
        }

        // For each active policy stored in the kernel
        uint256 policiesLen = activePolicies.length;
        for (uint256 j; j < policiesLen; ++j) {
            // Get the policy.
            Policy policy = activePolicies[j];

            // Deactivate the policy before changing kernel.
            policy.setActiveStatus(false);

            // Instruct the policy to change the kernel.
            policy.changeKernel(newKernel_);
        }
    }
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

Consider clearing the mappings or adding an additional piece of state to the Kernel to say that it has been migrated. This could be a boolean or the address of the new Kernel.


## Assessed type

Upgradable
There is no max fee threshold, the protocol devs could set the fee to the full 10000 ensuring no amount is sent to the user 
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blame/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/modules/PaymentEscrow.sol#L380-L388


# Vulnerability details

## Impact
All user assets in the safe contracts could be lost

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used 
manual analysis

## Recommended Mitigation Steps 
There should be a hardcoded max fee 


## Assessed type

Rug-Pull
Reentrancy in stopRent Function
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
An attacker can repeatedly call stopRent, initiating fund transfers before the state is updated. This allows the attacker to exploit reentrancy, potentially stealing more funds than the intended rental amounts.
## Proof of Concept
In the stopRent function of Stop.sol contract. If a lender repeatedly calls stopRent, it triggers the _reclaimRentedItems function. The vulnerability arises because the STORE.removeRentals function, responsible for updating the state, is called after transferring funds in the _reclaimRentedItems function. This creates a window of opportunity for an attacker to exploit reentrancy and steal more funds than intended.

 1. Lender calls stopRent multiple times, invoking _reclaimRentedItems with each 
   call.
2  _reclaimRentedItems transfers funds back to the lender.
3 .However, the STORE.removeRentals function, which updates the state, is called 
 after the fund transfer

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

    function stopRent(RentalOrder calldata order) external {
        // Check that the rental can be stopped.
        _validateRentalCanBeStoped(order.orderType, order.endTimestamp, order.lender);


        // Create an accumulator which will hold all of the rental asset updates, consisting of IDs and
        // the rented amount. From this point on, new memory cannot be safely allocated until the
        // accumulator no longer needs to include elements.
        bytes memory rentalAssetUpdates = new bytes(0);


        // Check if each item in the order is a rental. If so, then generate the rental asset update.
        // Memory will become safe again after this block.
        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
                );
            }
        }


        // Interaction: process hooks so they no longer exist for the renter.
        if (order.hooks.length > 0) {
            _removeHooks(order.hooks, order.items, order.rentalWallet);
        }


        // 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);


        // Interaction: Remove rentals from storage by computing the order hash.
        STORE.removeRentals(
            _deriveRentalOrderHash(order),
            _convertToStatic(rentalAssetUpdates)
        );


        // Emit rental order stopped.
        _emitRentalO
A malicious borrower can hijack any NFT with `permit()` function he rents.
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

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

1. **ERC-4494: Permit for ERC-721 NFTs**: ERC721-Permit is very similar to ERC20-permit (EIP-2612). ERC721 Permit adds a new `permit()` function. It allows user can sign an ERC721 approve transaction off-chain producing a signature that anyone could use and submit to the permit function. When permit is executed, it will execute the approve function. This allows for meta-transaction support of ERC721 transfers, but it also simply gets rid of the annoyance of needing two transactions: `approve` and `transferFrom`. Additionally, ERC721-Permit, just like ERC20 permit, prevents misuse and replay attacks. A replay attack is when a valid signature is used several times or in places where it's not intended to be used in.

    You can find an implementation of it here, by uniswap: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/ERC721Permit.sol


---
### The Vulnerability & Exploitation Steps

---

ReNFT doesn't account for ERC721 implementing the `permit()` function, allowing a malicious borrower to hijack the token by producing a signature and feeding it to the `permit()` function requesting it to approve his address to transfer the token

**Exploitation Steps**

1. The attacker rents the NFT token in his rental safe
2. The attacker creates a signature which he will need to feed to the `permit()` function. The signature is a signed data including info like: 1. the deadline (until when the signature would be valid), 2. the token ID he wants to approve his address to spend, 3. the spender, in this case it's the attacker's address (Look at the PoC to see how it's generated)
3. The attacker calls `permit()` funtion and gives it the signature along with the deadline, approved address and the spender.
4. The `permit()` function will reach out to the attacker's rental safe (since it is the owner of the token and since it's a smart contract verifying the signature (EIP-1271) ), asking it if the signature is valid
5. The rental safe will verify the signature and confirm that it's the attacker (owner of the safe) who signed the transaction
6. `permit()` will approve the attacker's address to spend the token on behalf of the attacker's safe (token is owned by the safe)
7. Attacker will then transfer the token out of his safe.

---
### Proof of concept
---
To run the PoC, you'll need to do the following:
1. You'll need to add the following two files to the test/ folder:
    1. `SetupExploit.sol` -> Sets up everything from seaport, gnosis, reNFT contracts
    2. `Exploit.sol` -> The actual exploit PoC which relies on `SetupExploit.sol` as a base.
2. You'll need to run this command
    ```forge test --match-contract Exploit --match-test test_NFT_Permit_Exploit -vvv```

*Note: All of my 7 PoCs throughout my reports inclu
Kernel does not implement a 2 step process for updating the executor and admin addresses
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L277-L302


# Vulnerability details

## Impact
The `executor` and `admin` are the addresses which control the `Kernel` and by extension the protocols configuration. The `executor` is the only address that can make updates to the kernel (such as adding/removing policies and modules) through calling `executeAction()` and the `admin` is able to grant and revoke roles.

When an `executor` calls `executeAction()` to update the `executor` or `admin` address, the address is immediately updated. If there is an error in updating these address the protocol could lose control of the kernel or give admin access to an unintended address.

## Proof of Concept
Consider an example where the `executor` address is updated to the wrong address mistakenly. In the non-malicious case there is no way to get control back of the kernel. In the malicious case the new `executor` could remove policies (such as the `Guard`) policy or add their own policies and modules.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Introduce a 2-step verification process for updates to the `executor` or `admin` addresses. That process could look something like this:

```
// add new variables to keep track of the next appointed addresses
address nextExecutor;
address nextAdmin;

...

function executeAction(Actions action_, address target_) external onlyExecutor {
        if (action_ == Actions.InstallModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _installModule(Module(target_));
        } else if (action_ == Actions.UpgradeModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _upgradeModule(Module(target_));
        } else if (action_ == Actions.ActivatePolicy) {
            ensureContract(target_);
            _activatePolicy(Policy(target_));
        } else if (action_ == Actions.DeactivatePolicy) {
            ensureContract(target_);
            _deactivatePolicy(Policy(target_));
        } else if (action_ == Actions.MigrateKernel) {
            ensureContract(target_);
            _migrateKernel(Kernel(target_));
        } else if (action_ == Actions.ChangeExecutor) {
            nextExecutor = target_
        } else if (action_ == Actions.ChangeAdmin) {
            nextAdmin = target_;
        }

        emit Events.ActionExecuted(action_, target_);
    }

function claimNewExecutor() public {
  require(nextExecutor == msg.sender, "Message sender is not the next executor");
  executor = msg.sender;
  nextExecutor = address(0);
}

function claimNewAdmin() public {
  require(nextAdmin == msg.sender, "Message sender is not the next admin");
  admin = msg.sender;
  nextAdmin = address(0);
}

...
```





## Assessed type

Other
Memory Overflow in addRentals Function
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

The addRentals function in the Storage.sol contract might be susceptible to a memory overflow issue when handling a large amount of input data. The absence of constraints or special handling for such scenarios raises concerns about the potential for memory-related vulnerabilities.
## Impact
This vulnerability could potentially lead to out-of-gas errors or unexpected behavior when processing a large number of rental asset updates in the addRentals function. An attacker might exploit this to disrupt the normal functioning of the contract.


## Proof of Concept

Title:
Potential Memory Overflow in addRentals Function

Links to affected code:

Storage.sol - addRentals function
Vulnerability details:
The addRentals function in the Storage.sol contract might be susceptible to a memory overflow issue when handling a large amount of input data. The absence of constraints or special handling for such scenarios raises concerns about the potential for memory-related vulnerabilities.

solidity
Copy code
// Example code snippet highlighting the concern
function addRentals(
    bytes32 orderHash,
    RentalAssetUpdate[] memory rentalAssetUpdates
) external onlyByProxy permissioned {
    // ... (existing code)

    // Concern: No explicit check for potential memory overflow here

    // ... (existing code)
}
Impact:
This vulnerability could potentially lead to out-of-gas errors or unexpected behavior when processing a large number of rental asset updates in the addRentals function. An attacker might exploit this to disrupt the normal functioning of the contract.

Proof of Concept:
1. Code Analysis:
The vulnerable portion of the code is identified in the addRentals function, particularly in handling a potentially large array of rentalAssetUpdates.

## Tools Used

## Recommended Mitigation Steps

```solidity
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

// ... (existing imports)

contract Storage is Proxiable, Module, StorageBase {
    using RentalUtils for address;

    // ... (existing code)

    /**
     * @notice Adds an order hash to storage. Once an order hash is added to storage,
     *         the assets contained within are considered actively rented. Additionally,
     *         rental asset IDs are added to storage which creates a blocklist on those
     *         assets. When the blocklist is active, the protocol guard becomes active on
     *         them and prevents transfer or approval of the assets by the owner of the
     *         safe.
     *
     * @param orderHash          Hash of the rental order which is added to storage.
     * @param rentalAssetUpdates Asset update structs which are added to storage.
     */
    function addRentals(
        bytes32 orderHash,
        RentalAssetUpdate[] memory rentalAssetUpdates
    ) external onlyByProxy permissioned {
A SINGLE MALICOUS LENDER CAN REVERT THE `Stop.stopRentBatch` TRANSACTION INSIDE THE `onERC721Received` HOOK WHILE RECEIVING THE `ERC721` TOKENS BACK
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/policies/Stop.sol#L353
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L90-L100
https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/packages/Reclaimer.sol#L32-L34


# Vulnerability details

## Impact

The `Stop.stopRentBatch` function is used to `stop` a batch of rentals by providing an array of `RentalOrder` structs to the function. 

For each of the `RentalOrders` in the struct the `Stop._reclaimRentedItems` function is called as shown below:

```solidity
            _reclaimRentedItems(orders[i]);
```

The `_reclaimRentedItems` function calls the `Stop.reclaimRentalOrder` function via a `delegateCall` from the `Gnosis safe`. In the `reclaimRentalOrder` function the `ERC721` or `ERC1155` is transferred to the `lender` for each of the `Items` as shown below:

```solidity
        // Transfer each item if it is a rented asset.
        for (uint256 i = 0; i < itemCount; ++i) { //@audit-info - struct Item { ItemType itemType; SettleTo settleTo; address token; uint256 amount; uint256 identifier; }
            Item memory item = rentalOrder.items[i]; //@audit-info - cache each item

            // Check if the item is an ERC721.
            if (item.itemType == ItemType.ERC721) //@audit-info - enum ItemType { ERC721, ERC1155, ERC20 }
                _transferERC721(item, rentalOrder.lender); //@audit-info - transfer the ERC721 to the lender

            // check if the item is an ERC1155.
            if (item.itemType == ItemType.ERC1155)
                _transferERC1155(item, rentalOrder.lender); //@audit-info - transfer the ERC1155
        }
```

Let's consider the case of `ERC721`. Here the `_transferERC721` function call is as follows:

```solidity
    function _transferERC721(Item memory item, address recipient) private {
        IERC721(item.token).safeTransferFrom(address(this), recipient, item.identifier);
    }
```

The `safeTransferFrom` is called on the ERC721 contract to transfer the token to the `lender`. Since the `ERC721.safeTransferFrom` calls the `onERC721Received` hook on the `lender`, a malicious lender can revert this transaction by calling `revert` inside the `onERC721Received` hook. This will revert the entire `Stop.stopRentBatch` batch transaction thus not allowing other `lenders` to receive their respective `ERC721` and `ERC1155` tokens.

As a result each of the `rentalOrders` will have to be `stopped` by calling them individually via the `Stop.stopRent` function. Hence there will be delay in `lenders` getting their `NFTs` back since each order has to be `stopped` individually. This could incur monetary loss to the `lender` since he could have used the `NFTs` for another purpose (`such as gaming`) if he received the NFT earlier. And this is not good for the user experience as well. The users might lose the trust on
The return value of an external call is not stored in a local or state variable.
Audit: 2024-01-renft-findings
Severity: MEDIUM

# Lines of code

https://github.com/re-nft/smart-contracts/blob/3ddd32455a849c3c6dc3c3aad7a33a6c9b44c291/src/Kernel.sol#L548


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The return value of an external call is not stored in a local or state variable.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

 for (uint256 i; i < depLength; ++i) {
            // Reconfigure its dependencies.
            dependents[i].configureDependencies();
        }

 function configureDependencies()
        external
        virtual
        onlyKernel
        returns (Keycode[] memory dependencies)
    {}

## Tools Used

## Recommended Mitigation Steps
Ensure that all the return values of the function calls are used.


## Assessed type

Context