xanoutas/solidity-security-findings huggingface dataset:
Uninialized or front-runnable `.init` function in proxy implementation contract
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L17


# Vulnerability details

# Uninialized or front-runnable `.init` function in proxy implementation contract

## Impact
DoS for all users' smart account proxies leading to locked funds forever.

## Proof of Concept
Nowhere in the code the SmartAccount.sol implementation contract is initialized by calling the `.init` function. This means that the owner of the SmartAccount.sol implementation contract is unknown (as it is set in the `.init` function) or the `.init` function may not even be called - 
can be forgotten by the team and later called or front-runned by an attacker. This will give an external account control over the implementation control such as executing any transaction from it via `call` and `delegatecall`.

If that happens, the attacker can immediately sign and execute a transaction directly on the SmartAccount.sol implementation contract (via `call`) using `.execTransaction` and make a `delegatecall` by setting the tx.operation to 1 and tx.to to a contract containing a `selfdestruct` operation. This will block all proxies and stuck all users's funds forever due to DoS and non-upgradeability as the `updateImplementation` mechanism is in the destroyed SmartAccount.sol implementation contract.

## Tools Used
Manual review

## Recommended Mitigation Steps
The standard way a factory pattern works is that the factory deploys the implementation contract it its constructor and then calls `init` to become the `owner` of this implementation contract. As the factory does not implement a function such as f.e. `executeArbitraryTransaction` there is no way the `.execTransaction` is called directly on the `SmartAccount` by the owner. Even more that way there can not be a valid signature to pass the `checkSignatures` validation.

Create the implementation contract in the [SmartAccountFactory.constructor](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol) instead of externally passing it. Then call the `.init` function on the newly deployed implementation contract and set the owner to be the `SmartAccountFactory` address (`address(this)`)

```
-   constructor(address _baseImpl) {
-       require(_baseImpl != address(0), "base wallet address can not be zero");
-       _defaultImpl = _baseImpl;
-   }
    
+   constructor(address _entryPoint, address _handler) {
+       _defaultImpl = address(new SmartAccount());
+       SmartAccount(_defaultImpl).init(address(this), _entryPoint, _handler)
+   }
```
Theft of funds under relaying the transaction
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L200
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L239
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L248


# Vulnerability details

## Description

The `execTransaction` function is designed to accept a relayed transaction with a transaction cost refund. At the beginning of the function, the `startGas` value is calculated as the amount of gas that the relayer will approximately spend on the transaction initialization, including the base cost of `21000` gas and the cost per calldata byte of `msg.data.length * 8` gas. At the end of the function, the total consumed gas is calculated as `gasleft() - startGas` and the appropriate refund is sent to the relayer.

An attacker could manipulate the calldata to increase the refund amount while spending less gas than what is calculated by the contract. To do this, the attacker could provide calldata with zero padded bytes of arbitrary length. This would only cost 4 gas per zero byte, but the refund would be calculated as 8 gas per calldata byte. As a result, the refund amount would be higher than the gas amount spent by the relayer.

### Attack scenario

Let’s a smart wallet user signs a transaction. Some of the relayers trying to execute this transaction and send a transaction to the `SmartAccount` contract. Then, an attacker can frontrun the transaction, changing the transaction calldata by adding the zeroes bytes at the end.

So, the original transaction has such calldata:

```sol=
abi.encodeWithSignature(RelayerManager.execute.selector, (...))
```

The modified (frontrun) transaction calldata:

```sol=
// Basically, just add zero bytes at the end
abi.encodeWithSignature(RelayerManager.execute.selector, (...)) || 0x00[]
```

### PoC

The PoC shows that the function may accept the data with redundant zeroes at the end. At the code above, an attacker send a 100_000 meaningless zeroes bytes, that gives a `100_000 * 4 = 400_000` additional gas refund. Technically, it is possible to pass even more zero bytes.

```solidity=
pragma solidity ^0.8.12;

contract DummySmartWallet {
    function execTransaction(
        Transaction memory _tx,
        uint256 batchId,
        FeeRefund memory refundInfo,
        bytes memory signatures
    ) external {
        // Do nothing, just test that data with appended zero bytes are accepted by Solidity
    }
}

contract PoC {
    address immutable smartWallet;

    constructor() {
        smartWallet = address(new DummySmartWallet());
    }

    // Successfully call with original data
    function testWithOriginalData() external {
        bytes memory txCalldata = _getOriginalTxCalldata();

        (bool success, ) = smartWallet.call(txCalldata);
        require(success);
    }

    // Successf
Lack of owner verification in EIP-1271 signature check
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L218
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342


# Vulnerability details

## Description

In the `checkSignatures` there are checks that the signer is the account owner, but in the case of EIP-1271 signature check there are no such checks:

```solidity=
// If v is 0 then it is a contract signature
// When handling contract signatures the address of the contract is encoded into r
_signer = address(uint160(uint256(r)));

// Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
    // This check is not completely accurate, since it is possible that more signatures than the threshold are send.
    // Here we only check that the pointer is not pointing inside the part that is being processed
    require(uint256(s) >= uint256(1) * 65, "BSA021");

    // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
    require(uint256(s) + 32 <= signatures.length, "BSA022");

    // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
    uint256 contractSignatureLen;
    // solhint-disable-next-line no-inline-assembly
    assembly {
        contractSignatureLen := mload(add(add(signatures, s), 0x20))
    }
    require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");

    // Check signature
    bytes memory contractSignature;
    // solhint-disable-next-line no-inline-assembly
    assembly {
        // The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
        contractSignature := add(add(signatures, s), 0x20)
    }
    require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");
```

So everyone can sign any transaction using the EIP-1271 signature validation method and convince the wallet that the valid signature was verified.

## Impact

The complete absence of signature verification, and as a result, the possibility of performing any transaction by a third party.

## Recommended Mitigation Steps

Add the following check into the EIP-1271 signature check logic:

```solidity=
require(_signer == owner, "INVALID_SIGNATURE");
```
Replay attack on different batchId
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L194
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L212
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L218


# Vulnerability details

## Description

The `execTransaction` function includes an input parameter called `batchId` that is used to determine the nonce which is included in the data signed by the owner. However, `batchId` is **not** part of the signed data. This allows any third party to replay a signed transaction using a different `batchId` value, as the nonce values will be the same.

## Impact

Possibility of replay of any transaction for which there exists a `batchId` with corresponding nonce value.

## Recommended Mitigation Steps

Add `batchId` into `Transaction` struct and include its value into the tx hash preimage in `encodeTransactionData` function.
Front-running "deployCounterFactualWallet"
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33


# Vulnerability details

## Description

The `deployCounterFactualWallet` function deploys a smart wallet using the `create2` function with a salt value that depends on the `_owner` and `_index` parameters. However, the address derivation for the deployed wallet does not depend on the `_entryPoint` and `_handler` parameters, which are used during initialization. This allows an attacker to front run the transaction and deploy a smart contract with their own `_handler` and `_entryPoint`, but same address. The attacker can then call the `execFromEntryPoint` function on the smart wallet, allowing them to execute any desired logic, such as changing the owner. As a result, users cannot rely on the address derivation in advance or deploy the wallet with the same address on different EVM chains.

## Impact

If users rely on the address derivation in advance or try to deploy the wallet with the same address on different EVM chains, any funds sent to the wallet could potentially be withdrawn by anyone else. All in all, it could lead to the theft of user funds.

## Recommended Mitigation Steps

Change formula with which `salt` is calculated to depends `_entryPoint` and `_handler`.
Attacker can take control over each SmartAccount proxy and steal all users' funds
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342


# Vulnerability details

# Attacker can take control over each SmartAccount proxy and steal all users' funds

## Impact
All users' funds can be stolen by a single attacker (tx gas cost only)

## Proof of Concept
There are 2 main reasons for this vulnerability:

The `.checkSignatures` in [SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L302-L353) has a missing `require(_signer == owner, "INVALID_SIGNATURE");` check in the first `if`. What the `_signer` actually does on [line 342](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342) is to verify that ITS OWNERS (not the `owner` of the SmartAccount proxy, but the owners of the `_signer` in the case where the `_signer` is another smart contract wallet f.e. gnosis safe) have signed/approved the passed `data` with the `contractSignature`. But its never checked the `_signer` is actually the `owner` of the user's smart account proxy. Note that the `_signer` address is also an externally passed variable and has no validation on it so there is basically a call to an arbitrary (untrusted) contract.


This opens the opportunity for an attacker to call `.execTransaction` directly on each user's smart account proxy thorugh the fallback function via `delegatecall` and pass a transaction that either calls `setOwner` or simply transfer funds to a wallet of the attacker. Because of the missing validation attacker can pass any mock contract address in the expected position in the signature (the r). The attacker contract will return `EIP1271_MAGIC_VALUE` on `.isValidSignature` and since there is no check that the `_signer` on [line 342](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342) is the `owner` of the user's smart contract proxy, the execution will continue without reverting.

To sum up, any attacker can execute the following steps for no cost (except tx gas cost):
1. Create an attacker contract that
- mocks `isValidSignature(bytes,bytes)` and returns `EIP1271_MAGIC_VALUE`
2. Call `.execTransaction` on any user's smart account proxy with tx.to = attacker contract address, tx.operation = 0 and tx.value = proxy contract's balance
3. Place the attacker contract address at the expected position in `signatures` (in the R value) and set the V to 0
4. [line 342](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342) will check that the attacker contract returns `EIP1271_MAGIC_VALUE` and will not check whether the `_signer` (attacker contract) is the `owner`
5. `.execTransaction` will continue and call `.execute` on the `Executor` (which is inherited `Executor` -> `ModuleM
SmartAccount implementation contract can be destroyed by anyone
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L192
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L302-L353
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342


# Vulnerability details

# SmartAccount implementation contract can be destroyed by anyone

## Impact
Locking all user's funds forever due to DoS for all functions.

## Proof of Concept
There are 2 main reasons for this vulnerability:

1. The expected behaviour of interacting with the [SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol) contract is that all of its function will be called by proxies via `delegatecall` meaning that the execution context will always be the proxy contracts. But there is no modifier nor check (`require`/`if`) that functions are not called directly on the `SmartAccount` implementation contract via `call` such as [this one](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L27).

2. The `.checkSignatures` in [SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L302-L353) has a missing `require(_signer == owner, "INVALID_SIGNATURE");` check in the first `if`. What the `_signer` actually does on [line 342](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342) is to verify that ITS OWNERS (not the `owner` of the SmartAccount implementation contract, but the owners of the `_signer` in the case where the `_signer` is another smart contract wallet f.e. gnosis safe) have signed/approved the passed `data` with the `contractSignature`. But its never checked the `_signer` is actually the `owner` of the biconomy smart account implementation contract. Note that the `_signer` address is also an externally passed variable and has no validation on it so there is basically a call to an arbitrary (untrusted) contract.


This opens the opportunity for an attacker to call `.execTransaction` directly on the `SmartAccount` contract via `call` and pass a transaction that `delegatecall`s to a malicious contract that implements a `selfdestruct` operation. Because of the missing validation attacker can pass any mock contract address in the expected position in the signature (the r). The contract will return `EIP1271_MAGIC_VALUE` on `.isValidSignature` and since there is no check that the `_signer` on [line 342](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L342) is the `owner` of the implementation contract, the execution will continue without reverting. The SmartAccoun
SmartAccount implementation contract can be destroyed by owner
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L17


# Vulnerability details

# SmartAccount implementation contract can be destroyed by owner

## Impact
Locking users' funds forever due to DoS for all deployed smart account proxies. Neither implementation upgrade will be possible nor withdrawing funds.

## Proof of Concept
The expected behaviour of interacting with the [SmartAccount.sol](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol) contract is that all of its function will be called by proxies via `delegatecall` meaning that the execution context will always be the proxy contracts. But there is no modifier nor check (`require`/`if`) that functions are not called directly on the `SmartAccount` implementation contract via `call` such as [this one](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L27).

This opens the opportunity for the implementation contract owner to call `.execTransaction` directly on the `SmartAccount` contract via `call` and pass a transaction that `delegatecall`s to a malicious contract that implements a `selfdestruct` operation.


The `SmartAccount` implementation contract address is passed to the `SmartAccountFactory` through the `constructor` which means that the implementation contract is deployed before that and the owner is unknown.

## Tools Used
Manual review

## Recommended Mitigation Steps
The standard way a factory pattern works is that the factory deploys the implementation contract it its constructor and then calls `init` to become the `owner` of this implementation contract. As the factory does not implement a function such as f.e. `executeArbitraryTransaction` there is no way the `.execTransaction` is called directly on the `SmartAccount` by the owner. Even more that way there can not be a valid signature to pass the `checkSignatures` validation.

Create the implementation contract in the [SmartAccountFactory.constructor](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol) instead of externally passing it. Then call the `.init` function on the newly deployed implementation contract and set the owner to be the `SmartAccountFactory` address (`address(this)`)

```
-   constructor(address _baseImpl) {
-       require(_baseImpl != address(0), "base wallet address can not be zero");
-       _defaultImpl = _baseImpl;
-   }
    
+   constructor(address _entryPoint, address _handler) {
+       _defaultImpl = address(new SmartAccount());
+       SmartAccount(_defaultImpl).init(address(this), _entryPoint, _handler)
+   }
```
The owner's address is not declared as payable
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L51
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L172


# Vulnerability details

## Impact
If someone sends ether to the contract, it would be highly possible that the ether could be locked forever or never withdrawn by its intended recipient(Owner)

## Proof of Concept
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L51
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L172

## Tools Used
Manual

## Recommended Mitigation Steps
Add "payable" keyword to the owner state variable as well as to the "_owner" when assigning owner = _owner in init function
``` 
address payable public owner;

```

``` 

owner = payable(_owner)

```
when the 'v' value from a signature is 0, checkSignatures function can be tricked
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L314-L343


# Vulnerability details

## Impact
when the v value of a signature is = 0, the 'checkSignatures' in entryPoint function doesn't check if the signer is the owner of the wallet and assumes that it is from a contract, a malicious party could craft a signature with the v value = 0, and implement the 'ISignatureValidator' interface

```solidity
contract ISignatureValidatorConstants {
    // bytes4(keccak256("isValidSignature(bytes,bytes)")
    bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
}

abstract contract ISignatureValidator is ISignatureValidatorConstants {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param _data Arbitrary length data signed on the behalf of address(this)
     * @param _signature Signature byte array associated with _data
     *
     * MUST return the bytes4 magic value 0x20c13b0b when function passes.
     * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
     * MUST allow external calls
     */
    function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);
}

```
this allows them to effectively call any function on the wallet

## Proof of Concept
calling the function

```solidity
    function execTransaction(
        Transaction memory _tx,
        uint256 batchId,
        FeeRefund memory refundInfo,
        bytes memory signatures
    ) public payable virtual override returns (bool success) {
```
with value
```solidity
execTransaction(
            Transaction(address(this), 1 ether, "", Operation.Call, 0),
            2,
            FeeRefund(1,0,1,address(0), payable(address(this))),
"0x000000000000000000000000d8b934580fce35a11b58c6d73adee468a2833fa8000000000000000000000000d8b934580fce35a11b58c6d73adee468a2833fa800"
            
```
on a smart wallet address effectively transfers funds to the contract provided the wallet has funds and gas in the entryPoint contract

## Tools Used
slither

## Recommended Mitigation Steps
consider checking that the signer is the owner in the checkSignatures even if the v == 0