xanoutas/solidity-security-findings huggingface dataset:
Users with `DEPLOY` permission can grief each other through `CREATE2`
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L253-L267


# Vulnerability details

## Bug Description

In `ERC725XCore.sol`, the `_deployCreate2()` function uses Openzeppelin's [`Create2.deploy()`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Create2.sol#L31-L59) to deploy new contracts:

[ERC725XCore.sol#L253-L267](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L253-L267)

```solidity
    function _deployCreate2(
        uint256 value,
        bytes memory creationCode
    ) internal virtual returns (bytes memory newContract) {
        if (creationCode.length == 0) {
            revert ERC725X_NoContractBytecodeProvided();
        }

        bytes32 salt = BytesLib.toBytes32(creationCode, creationCode.length - 32);
        bytes memory bytecode = BytesLib.slice(creationCode, 0, creationCode.length - 32);
        address contractAddress = Create2.deploy(value, salt, bytecode);

        newContract = abi.encodePacked(contractAddress);
        emit ContractCreated(OPERATION_2_CREATE2, contractAddress, value, salt);
    }
```

Openzeppelin's `Create2.deploy()` then deploys the contract using `CREATE2` with the given `salt` and `bytecode`:

[Create2.sol#L53-L55](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Create2.sol#L53-L55)

```solidity
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
```

As seen from above, the address of the newly deployed contract solely relies on the provided `salt` and `bytecode`, which both come from `_deployCreate2()`'s `creationCode` parameter.

This becomes a problem when an LSP0 account is used alongside a LSP6 Key Manager, and multiple users are given the `DEPLOY` permission. Once a user's transaction to deploy a contract using `CREATE2` is broadcasted, the `creationCode` parameter can be viewed by anyone watching the public mempool.

This creates two issues:
1. **Stealing of a user's deposits or permissions.** If a user intends to create new contract and deposit some funds or add permissions to it, an attacker can front-run the user's transaction to steal his deposits or permissions:
    * User broadcasts two transactions:
      * The first transaction deploys a contract using `CREATE2` with a certain `creationCode`.
      * The second transaction deposits some LYX into the new address and grants it some permissions. This is possible as users can compute the address generated by `CREATE2` beforehand.
    * An attacker sees these pending transactions and front-runs them to deploy a contract for himself using `CREATE2` with the same `creationCode`.
    * The contract is deployed for the attacker first. As the same `creationCode` was used, this contract is deployed at the address the user had computed beforehand.
    * The user's `CREATE2` transaction reverts, but his second
possible revert due to improper subtraction in _extendableMsgData() of LSP17Extension.sol
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/9dbc96410b3052fc0fd9d423249d1fa42958cae8/contracts/LSP17ContractExtension/LSP17Extension.sol#L32
https://github.com/code-423n4/2023-06-lukso/blob/9dbc96410b3052fc0fd9d423249d1fa42958cae8/contracts/LSP17ContractExtension/LSP17Extension.sol#L38


# Vulnerability details

## Impact
in _extendableMsgData() of LSP17Extension.sol there is an arithmetic calculation 

```
msg.data[:msg.data.length - 52];
```
msg.data.length may be smaller than 52 in some instances. this can cause reverts in solidity 

## Proof of Concept
```
    function _extendableMsgData()
        internal
        view
        virtual
        returns (bytes calldata)
    {
        return msg.data[:msg.data.length - 52];
    }
```
https://github.com/code-423n4/2023-06-lukso/blob/9dbc96410b3052fc0fd9d423249d1fa42958cae8/contracts/LSP17ContractExtension/LSP17Extension.sol#L32-L39
## Tools Used
VS CODE 
## Recommended Mitigation Steps
ADD CHECKS FOR MSG.DATA.LENGTH > 52 before the arithmetic 





## Assessed type

Math
Allowing delegate call with `msg.value` in `executeBatch()` is dangerous
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L279-L324


# Vulnerability details

## Bug Description

`ERC725XCore`'s [`_execute()`](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L70-L118) function allows four types of operations: 
1. `CALL` for normal calls
2. `DELEGATECALL`
3. `CREATE`/`CREATE2` for contract deployment 
4. `STATICCALL`

The `_executeBatch()` function simply calls `_execute()` in a loop to perform multiple calls in a single transaction:

[ERC725XCore.sol#L146-L153](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L146-L153)

```solidity
        for (uint256 i = 0; i < operationsType.length; ) {
            result[i] = _execute(operationsType[i], targets[i], values[i], datas[i]);

            // Increment the iterator in unchecked block to save gas
            unchecked {
                ++i;
            }
        }
```

In `LSP0AccountCore.sol`, [`executeBatch()`](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L279-L324) calls `ERC725XCore._executeBatch()` without performing any kind of checks on what calls are being made. 

This is dangerous as a user could batch a call or contract deployment that sends LYX and a delegate call together. 

If the delegate call happens to call a function that is `payable`, `msg.value` in this delegate call will be the same as the normal call, leading to unintended `msg.value` reuse. Furthermore, if the function called using delegate call does outgoing transfers of funds, the user could potentially drain his own LSP0 account by accident.

## Impact

By batching a call/contract deployment that has `msg.value` and a delegate call togehter with `executeBatch()`, a user could unintentionally drain his own LSP0 account through reused `msg.value` in the delegate call.  

Given that LSP0 accounts are meant to be used by regular users and the effects of batching a delegate call with other calls is not immediately obvious, this is a risk that should be mitigated to protect users.

## Recommended Mitigation

In `executeBatch()`, ensure that `msg.value` is 0 when one of the batched calls is a delegate call. This would prevent `msg.value` from being reused across multiple calls. 


## Assessed type

call/delegatecall
Avoid using the same ERC-165 interface ID for URDs and their callers
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol#L259-L265


# Vulnerability details

## Bug Description

Contracts that implement the LSP-1 standard include `_INTERFACEID_LSP1` in their `supportsInterface()` function. This means that they have a `universalReceiver()` function that calls a Universal Receiver Delegate (URD), such as [`LSP1UniversalReceiverDelegateUP`](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol).

`LSP0ERC725Account` is an example of a URD caller:

[LSP0ERC725AccountCore.sol#L704](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L704)

```solidity
            interfaceId == _INTERFACEID_LSP1 ||
```

Similarly, URDs are also expected to include `_INTERFACEID_LSP1` in their `supportsInterface()` function, such as `LSP1UniversalReceiverDelegateUP`:

[LSP1UniversalReceiverDelegateUP.sol#L259-L265](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol#L259-L265)

```solidity
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override returns (bool) {
        return
            interfaceId == _INTERFACEID_LSP1 ||
            super.supportsInterface(interfaceId);
    }
```

Therefore, URD callers will check for `_INTERFACEID_LSP1` to determine if the address is a URD. `LSP0ERC725AccountCore`'s `universalReceiver()` function is an example of this behavior:

[LSP0ERC725AccountCore.sol#L479-L493](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L479-L493)

```solidity
            // Checking LSP1 InterfaceId support
            if (
                universalReceiverDelegate.supportsERC165InterfaceUnchecked(
                    _INTERFACEID_LSP1
                )
            ) {
                // calling {universalReceiver} function on URD appending the caller and the value sent
                resultDefaultDelegate = universalReceiverDelegate
                    .callUniversalReceiverWithCallerInfos(
                        typeId,
                        receivedData,
                        msg.sender,
                        msg.value
                    );
            }
```

However, this is an issue as there is no way to differentiate between URDs and their callers. This could potentially allow users to perform a transaction that requires a LSP-1 compatible contract on the receiving end, such a LSP7 token transfer, but with a URD as the receiver instead.

For example, a user could send LSP7 tokens to the `LSP1UniversalReceiverDelegateUP` contract, and the call would succeed as its contract supports `_INTERFACEID_LSP1` and has a `universalReceiver()` function.
Using `supportsERC165InterfaceUnchecked()` might break LSP functionality for certain contracts
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol#L480-L496


# Vulnerability details

## Bug Description

Throughout the codebase, the protocol uses the `supportsERC165InterfaceUnchecked()` function from Openzeppelin's [`ERC165Checker.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Checker.sol) to check for the support of ERC-165 interface IDs. 

However, `supportsERC165InterfaceUnchecked()` only checks if the call to `supportsInterface()` has a non-zero return value:

[ERC165Checker.sol#L116-L122](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Checker.sol#L116-L122)

```solidity
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
```

This might cause `supportsERC165InterfaceUnchecked()` to incorrectly return true for contracts that meet one of the following criteria:
* Has a function with the selector `0x01ffc9a7`, which clashes with `supportsInterface(bytes4)`. This function must have a non-zero return value.
* Contains a fallback function that returns a non-zero value.

In the protocol, `supportsERC165InterfaceUnchecked()` is mainly used to check if an address is compatible with LSP-1 before calling its `universalReceiver()` function. 

For example, `_notifyTokenReceiver()` in `LSP7DigitalAssetCore.sol` ensures that the address receiving tokens is capable of handling them:

[LSP7DigitalAssetCore.sol#L480-L496](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol#L480-L496)

```solidity
        if (
            ERC165Checker.supportsERC165InterfaceUnchecked(
                to,
                _INTERFACEID_LSP1
            )
        ) {
            ILSP1UniversalReceiver(to).universalReceiver(
                _TYPEID_LSP7_TOKENSRECIPIENT,
                lsp1Data
            );
        } else if (!allowNonLSP1Recipient) {
            if (to.code.length > 0) {
                revert LSP7NotifyTokenReceiverContractMissingLSP1Interface(to);
            } else {
                revert LSP7NotifyTokenReceiverIsEOA(to);
            }
        }
```

However, if the receiving contract meets one of the criteria mentioned above, `supportsERC165InterfaceUnchecked()` will incorrectly return true, even if the receiving contract does not support LSP-1. 

This would cause the transaction to revert when attempting to call `universalReceiver()` at the `to` address. Therefore, the `to` address will never be able to receive LSP7 tokens, even if `allowNonLSP1Recipient` is set to `true`.

Note that the scenario demonstrated above is only one of the many possible impacts. As `supportsERC165Inter
The owner of a `LSP0ERC725Account` can become the owner again after renouncing ownership
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol#L176-L178


# Vulnerability details

## Bug Description

The `renounceOwnership()` function allows the owner of a `LSP0ERC725Account` to renounce ownership through a two-step process. When `renounceOwnership()` is first called, `_renounceOwnershipStartedAt` is set to `block.number` to indicate that the process has started:

[LSP14Ownable2Step.sol#L159-L167](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol#L159-L167)

```solidity
        if (
            currentBlock > confirmationPeriodEnd ||
            _renounceOwnershipStartedAt == 0
        ) {
            _renounceOwnershipStartedAt = currentBlock;
            delete _pendingOwner;
            emit RenounceOwnershipStarted();
            return;
        }
```

When `renounceOwnership()` is called again, the owner is then set to `address(0)`:

[LSP14Ownable2Step.sol#L176-L178](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol#L176-L178)

```solidity
        _setOwner(address(0));
        delete _renounceOwnershipStartedAt;
        emit OwnershipRenounced();
```

However, as `_pendingOwner` is only deleted in the first call to `renounceOwnership()`, an owner could regain ownership of the account after the second call to `renounceOwnership()` by doing the following:

1. Call `renounceOwnership()` for the first time to initiate the process.
1. Using `execute()`, perform a delegate call that overwrites `_pendingOwner` to his own address.
1. Call `renounceOwnership()` again to set the owner to `address(0)`.
 
As `_pendingOwner` is still set to the owner's address, he can call `acceptOwnership()` at anytime to regain ownership of the account.

## Impact

Even after the `renounceOwnership()` process is completed, an owner might still be able to regain ownership of an LSP0 account. 

This could potentially be dangerous if users assume that an LSP0 account will never be able to call restricted functions after ownership is renounced, as stated in [the following comment](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L652):

> Leaves the contract without an owner. Once ownership of the contract has been renounced, any functions that are restricted to be called by the owner will be permanently inaccessible, making these functions not callable anymore and unusable.

For example, if a protocol's admin is set to a `LSP0ERC725Account`, the owner could gain the community's trust by renouncing ownership. After the protocol has gained a significant TVL, the owner could then regain ownership of the account and proceed to rug the protocol.  

## Proof of Concept

The following Foundry test demonstrates how an owner can regain ownership of a `LSP0ERC725Account` after `renounceOwnership()` has been called twice:

```solidity
// S
Two-step ownership transfer process in `LSP0ERC725AccountCore` can be bypassed
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L560-L580


# Vulnerability details

## Bug Description

To transfer ownership of the `LSP0ERC725AccountCore` contract, the owner has to call `transferOwnership()` to nominate a pending owner. Afterwards, the pending owner must call `acceptOwnership()` to become the new owner.

When called by the owner, `transferOwnership()` executes the following logic:

[LSP0ERC725AccountCore.sol#L560-L580](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol#L560-L580)

```solidity
        address currentOwner = owner();

        // If the caller is the owner perform transferOwnership directly
        if (msg.sender == currentOwner) {
            // set the pending owner
            LSP14Ownable2Step._transferOwnership(pendingNewOwner);
            emit OwnershipTransferStarted(currentOwner, pendingNewOwner);

            // notify the pending owner through LSP1
            pendingNewOwner.tryNotifyUniversalReceiver(
                _TYPEID_LSP0_OwnershipTransferStarted,
                ""
            );

            // Require that the owner didn't change after the LSP1 Call
            // (Pending owner didn't automate the acceptOwnership call through LSP1)
            require(
                currentOwner == owner(),
                "LSP14: newOwner MUST accept ownership in a separate transaction"
            );
        } else {
```

The `currentOwner == owner()` check ensures that `pendingNewOwner` did not call `acceptOwnership()` in the `universalReceiver()` callback. However, a malicious contract can bypass this check by doing the following in its `universalReceiver()` function:

* Call `acceptOwnership()` to gain ownership of the LSP0 account.
* Do whatever he wants, such as transferring the account's entire LYX balance to himself. 
* Call `execute()` to perform a delegate call that does either of the following:
  * Delegate call into a contract that self-destructs, which will destroy the account permanently.
  * Otherwise, use delegate call to overwrite `_owner` to the previous owner.
  
This defeats the entire purpose of a two-step ownership transfer, which should ensure that the LSP0 account cannot be lost in a single call if the owner accidentally calls `transferOwnership()` with the wrong address.

## Impact

Should `transferOwnership()` be called with the wrong address, the address could potentially bypass the two-step ownership transfer process to destroy the LSP0 account in a single transaction.

## Proof of Concept

The following Foundry test demonstrates how an attacker can drain the LYX balance of an LSP0 account in a single transaction when set to the pending owner in `transferOwnership()`:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "../../../contracts/LSP0ERC725Account/LSP0ERC725Account.sol";
LSP8 and LSP9's ERC-165 interface ID differs from their specification
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP7DigitalAsset/LSP7Constants.sol#L4-L5
https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol#L4-L5


# Vulnerability details

## Bug Description

According to [LSP7's specification](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-7-DigitalAsset.md#specification), the [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID for LSP7 token contracts should be `0x5fcaac27`:

> ERC165 interface id: `0x5fcaac27`

However, `_INTERFACEID_LSP7` has a different value in the code:

[LSP7Constants.sol#L4-L5](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP7DigitalAsset/LSP7Constants.sol#L4-L5)

```solidity
// --- ERC165 interface ids
bytes4 constant _INTERFACEID_LSP7 = 0xda1f85e4;
```

Similarly, LSP8's interface ID should be `0x49399145` according to [LSP8's specification](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#specification):

> ERC165 interface id: `0x49399145`

However, `_INTERFACEID_LSP8` has a different value in the code:

[LSP8Constants.sol#L4-L5](https://github.com/code-423n4/2023-06-lukso/blob/main/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol#L4-L5)

```solidity
// --- ERC165 interface ids
bytes4 constant _INTERFACEID_LSP8 = 0x622e7a01;
```

These constants are used in `supportsInterface()` for the `LSP7DigitalAsset` and `LSP8IdentifiableDigitalAsset` contracts.

## Impact

Protocols that check for LSP7/LSP8 compatibility using the ERC-165 interface IDs declared in the specification will receive incorrect return values when calling `supportsInterface()`.

## Recommended Mitigation

Ensure that the interface ID declared in the code matches their respective ones in their specifications.


## Assessed type

Error
`_deployCreate()`/`_deployCreate2()` will not work on ZKSync Era
Audit: 2023-06-lukso-findings
Severity: MEDIUM

# Lines of code

https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L221-L245


# Vulnerability details

## Bug Description

In the contest's [Scoping Details](https://github.com/code-423n4/2023-06-lukso/tree/main#scoping-details), the sponsor states that Universal Profiles might eventually be deployed across multiple chains:

> **Is it multi-chain?**
> 
> LUKSO itself is not a multi-chain. The lsp-smart-contracts are initially intended to be used on the LUKSO network. However, the Universal Profile of a user could be deployed at the same address across multiple chains (using for instance Nick's method), to enable the user to use the same UP across multiple networks / chains and offer a different user experience.

According to [ZKSync Era's documentation](https://era.zksync.io/docs/reference/architecture/differences-with-ethereum.html#create-create2), the bytecode of a contract must be known beforehand by the compiler for the `CREATE`/`CREATE2` opcode to work:

> To guarantee that `create`/`create2` functions operate correctly, the compiler must be aware of the bytecode of the deployed contract in advance.

However, in [`_deployCreate()`](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L215-L245) and [`_deployCreate2()`](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L247-L267), the contract's bytecode is provided by the caller as an argument to `execute()`:

[ERC725XCore.sol#L221-L245](https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/implementations/contracts/ERC725XCore.sol#L221-L245)

```solidity
    function _deployCreate(
        uint256 value,
        bytes memory creationCode
    ) internal virtual returns (bytes memory newContract) {
        // Some code here...

        address contractAddress;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            contractAddress := create(value, add(creationCode, 0x20), mload(creationCode))
        }

        // Some code here...
    }
```

Therefore, these functions will not work on ZKSync Era.

## Impact

If Universal Profiles are deployed on ZKSync Era, users will not be able to deploy contracts through `LSP0ERC725Account`.

## Recommended Mitigation

Unfortunately, this is a limitation of the ZKSync Era chain that cannot be avoided. 


## Assessed type

Other