xanoutas/solidity-security-findings huggingface dataset:
Signature Replay Attack when EntryPoint contract is changed
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/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L77-L90


# Vulnerability details

# Signature Replay Attack when EntryPoint contract is changed

## Impact
User operations can be replayed on smart accounts once the EntryPoint is changed. This can lead to user's loosing funds or any unexpected behaviour that transaction replay attacks usually lead to.

## Proof of Concept
As specified by the [EIP4337](https://eips.ethereum.org/EIPS/eip-4337) standard `to prevent replay attacks (both cross-chain and multiple EntryPoint implementations), the signature should depend on chainid and the EntryPoint address.`. In [VerifyingSingletonPaymaster.sol#getHash](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L77-L90) the EntryPoint contract address is missing which means that the same UserOperation can be replayed on the same smart contract account once the EntryPoint is changed.

## Tools Used
Manual review

## Recommended Mitigation Steps
Add the EntryPoint contract address in the calculation of the UserOperation hash in [VerifyingSingletonPaymaster.sol#getHash](https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L77-L90)

```
    function getHash(UserOperation calldata userOp)
    public view returns (bytes32) { // @audit change to view
        //can't use userOp.hash(), since it contains also the paymasterAndData itself.
        return keccak256(abi.encode(
                userOp.getSender(),
                userOp.nonce,
                keccak256(userOp.initCode),
                keccak256(userOp.callData),
                userOp.callGasLimit,
                userOp.verificationGasLimit,
                userOp.preVerificationGas,
                userOp.maxFeePerGas,
                userOp.maxPriorityFeePerGas
		address(entryPoint) // @audit add entry point contract address (inherited from BasePaymaster)
            ));
    }
```
Deny of service in `SmartAccountFactory`
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/SmartAccountFactory.sol#L34


# Vulnerability details

## Impact
The `salt` used for create2 does not include information from the `init` method, so it is vulnerable to front-running.

## Proof of Concept
it's impossible to override an existing contract in Ethereum. From [EIP-684](https://github.com/ethereum/EIPs/issues/684):

> If a contract creation is attempted, due to either a creation transaction or the CREATE (or future CREATE2) opcode, and the destination address already has either nonzero nonce, or nonempty code, then the creation throws immediately, with exactly the same behavior as would arise if the first byte in the init code were an invalid opcode. This applies retroactively starting from genesis.

For that reason if the salt is repeated, the call fill fault.

```bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));```

If the user want to deploy a wallet using `deployCounterFactualWallet` with the `_owner=A` and `_index=0`, an attacker can listen the memory pool and deploy with an higer gas, a wallet with the same owner and index, **but with different `_entryPoint` and `_handler`** so the attacker can deny the original call, and make unwanted actions in the proxy.

```BaseSmartAccount(proxy).init(_owner, _entryPoint, _handler);```

## Recommended Mitigation Steps
Include all received fields in the salt calculation.
Attacker can gain control of counterfactual wallet
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-L45


# Vulnerability details

A counterfactual wallet can be used by pre-generating its address using the `SmartAccountFactory.getAddressForCounterfactualWallet` function. This address can then be securely used (for example, sending funds to this address) knowing in advance that the user will later be able to deploy it at the same address to gain control.

However, an attacker can deploy the counterfactual wallet on behalf of the owner and use an arbitrary entrypoint:

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

```solidity
function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){
    bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));
    bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
    // solhint-disable-next-line no-inline-assembly
    assembly {
        proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
    }
    require(address(proxy) != address(0), "Create2 call failed");
    // EOA + Version tracking
    emit SmartAccountCreated(proxy,_defaultImpl,_owner, VERSION, _index);
    BaseSmartAccount(proxy).init(_owner, _entryPoint, _handler);
    isAccountExist[proxy] = true;
}
```

As the entrypoint address doesn't take any role in the address generation (it isn't part of the salt or the init hash), then the attacker is able to use any arbitrary entrypoint while keeping the address the same as the pre-generated address.

## Impact

After the attacker has deployed the wallet with its own entrypoint, this contract can be used to execute any arbitrary call or code (using `delegatecall`) using the `execFromEntryPoint` function:

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

```solidity
function execFromEntryPoint(address dest, uint value, bytes calldata func, Enum.Operation operation, uint256 gasLimit) external onlyEntryPoint returns (bool success) {        
    success = execute(dest, value, func, operation, gasLimit);
    require(success, "Userop Failed");
}
```

This means the attacker has total control over the wallet, it can be used to steal any pre-existing funds in the wallet, change the owner, etc.

## PoC

In the following test, the attacker deploys the counterfactual wallet using the `StealEntryPoint` contract as the entrypoint, which is then used to steal any funds present in the wallet.

```solidity
contract StealEntryPoint {
    function steal(SmartAccount wallet) public {
        uint256 balance = address(wallet).balance;

        wallet.execFromEntryPoint(
            msg.sender, // address dest
            balance, // uint value
SmartAccount wallet creation can be backdoored
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#L53


# Vulnerability details

At wallet creation time, an attacker can temporarily swap the address of the entrypoint to install a backdoor in the form of a registered module in the wallet.

Since wallets don't necessarily need to be created by their owners, an attacker can frontrun the wallet creation or trick the user using phishing techniques and modify the `_entryPointAddress` parameter with a custom version.

Entrypoints have total control of the wallet through the `execFromEntryPoint`. This means that the attacker can use a contract that acts as a temporal entrypoint to install and register a module in the wallet. This module would then sit dormant in the wallet until the attacker decides to use it against the user.

After the backdoor module is installed, the same temporal entrypoint implementation can be used to replace the wallet's entrypoint back to the original legit implementation. This would leave a wallet initialized with the intended owner and entrypoint, but with the backdoor already installed.

In summary, an attack can create a backdoored wallet on behalf of a user by swapping the entrypoint with an implementation that registers the backdoored module and then replaces the entrypoint implementation back to the original version (see PoC for more details).

## Impact

The installed module acts as a backdoor that has total control of the user's wallet, as any module can execute arbitrary code in the context of the `SmartAccount` contract through the `execFromEntryPoint` function.

The attacker can then wait for the right moment to steal any funds present in the wallet or compromise any integration associated with the wallet.

Note that this attack is also possible using the counterfactual wallet generation (`SmartAccountFactory.deployCounterFactualWallet`) as the entrypoint address is not part of the salt or init hash passed to create2.

## PoC

In the following test, an attacker creates Bob's wallet by replacing the entrypoint with a version that is used to install the backdoor. The steps are:

1. Deploy `BadEntrypoint` and `ModuleBackdoor`
2. Frontrun wallet's creation using `BadEntrypoint` as the entrypoint. Owner and handler are the same.
3. Call `BadEntrypoint.enableBackdoor` which will install the backdoored module by calling `ModuleManager.enableBackdoor` from the entrypoint and then replacing the entypoint itself with the real entrypoint implementation.

```solidity
contract ModuleBackdoor {
    function hack() external {
        // we can call execTransactionFromModule from this module to execute arbitrary code
    }
}

contract BadEntrypoint {
    function enableBackdoor(SmartAccount wallet, address module, address realEntrypoint) external {
        wallet.execFromEntryPoint(
            address(wallet), // address dest
            0, // uint value
            abi.encodeWithSelect
An attacker can create a smart contract wallet with a malicious config and the address that the user expects his smart contract to have 
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

## Issue
A deployCounterFactualWallet function in the SmartAccountFactory.sol uses create2 command to deploy a smart contract wallet with the address that can be computed before a transaction. A  problem with the function is that it doesn't include the config parameters in the create2 creation salt.

That means that when a user tries to deploy a new wallet, an attacker can front-run his transaction to create a smart contract wallet with a malicious config and the same address that the user expects his smart contract to have.

One of the config parameters is named _entrypoint. When an attacker sets this parameter to his address, he can call the smart contracts wallet's setOwner function to change the wallet owner's address to his address(shown in the POC). 

## Impact

After trying to create wallet if a user only checks that the smart contract wallet exists with the expected address and starts using the wallet. In this case, if an attacker managed to front-run the user's transaction, an attacker can take over the contract, stealing all deposited funds.

## Mitigation

Proposed mitigation is to include config parameters in the create2 salt, that way attacker can't create wallet with user expected address but different config. Example function below:

``` 
function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){
        bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index)), _entryPoint, _handler));
        bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
        // solhint-disable-next-line no-inline-assembly
        assembly {
            proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
        }
        require(address(proxy) != address(0), "Create2 call failed");
        // EOA + Version tracking
        emit SmartAccountCreated(proxy,_defaultImpl,_owner, VERSION, _index);
        BaseSmartAccount(proxy).init(_owner, _entryPoint, _handler);
        isAccountExist[proxy] = true;
    }
 ```
## Poc

POC below will show how attacker can set himself as the wallet's owner, when wallets entrypoint address is the attackers address.

Steps:

    1. Clone the repository: git clone git@github.com:code-423n4/2023-01-biconomy.git

    2. In the folder scw-contracts, run command: yarn

    3. Create a scw-contracts/.secret file and enter your mnemonic

    4. In the folder scw-contracts, run command: npx hardhat compile

    5. Create file named walletPoc.ts in the folder the /2023-01-biconomy/scw-contracts/test/smart-wallet

    6. Copy code below to created file

    7. In the folder scw-contracts, run command: npx hardhat test ./test/smart-wallet/walletPoc.ts 

After running poc, following logs show that ow
SmartAccount authorization can be bypassed using a contract signature
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#L314-L343


# Vulnerability details

# SmartAccount authorization can be bypassed using a contract signature

The `SmartAccount` wallet supports contract signatures defined by EIP1271, similar to how Gnosis Safe does. Transactions to the wallet can be authorized by a contract that implements the `ISignatureValidator` interface. This feature is implemented in the `checkSignatures` function, around lines 314-343:

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

```solidity
if(v == 0) {
  // 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");
}
```

The issue here is that, even though the signature is validated against the contract, there's no relation or enforcement between the signer contract and the owner of the wallet. This means that signature checks can be easily bypassed using a contract signature.

## Impact

This represents a critical issue since the authorization of any wallet can be easily bypassed using a dummy contract that acts as the signer. 

This would let an attacker access and execute anything on any SmartAccount wallet. By calling the `execTransaction` function on the `SmartAccount` contract, an attacker can trigger a `call` or `delegatecall` in the context of the wallet and essentially execute any arbitrary code.

## PoC

In the follo
`tokenGasPriceFactor` in `FeeRefund` struct can be malleable in calls to `execTransaction`
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#L424-L446


# Vulnerability details

The `execTransaction` function present in the `SmartAccount` contract is susceptible to a malleability attack.

This is due to the fact that the `encodeTransactionData` function, which is used to encode the transaction data that is part of the hash used to verify the signature, does not include the `refundInfo.tokenGasPriceFactor` property.

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

```solidity
function encodeTransactionData(
    Transaction memory _tx,
    FeeRefund memory refundInfo,
    uint256 _nonce
) public view returns (bytes memory) {
    bytes32 safeTxHash =
        keccak256(
            abi.encode(
                ACCOUNT_TX_TYPEHASH,
                _tx.to,
                _tx.value,
                keccak256(_tx.data),
                _tx.operation,
                _tx.targetTxGas,
                refundInfo.baseGas,
                refundInfo.gasPrice,
                refundInfo.gasToken,
                refundInfo.refundReceiver,
                _nonce
            )
        );
    return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
}
```

As a result, this property can be modified to any value and the signature will still be considered valid. This allows an attacker to frontrun the transaction, modify the `tokenGasPriceFactor` value, and resubmit the transaction with the same signature.

## Impact

The `tokenGasPriceFactor` property is used in the `handlePayment` function to adjust the payment value when the refund is made using ERC20 tokens:

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

```solidity
payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);
require(transferToken(gasToken, receiver, payment), "BSA012");
```

The value of `tokenGasPriceFactor` is used as a dividing factor to correct the gas calculation according to the token being used. This can potentially be exploited in two ways:

1. A griefer can modify the `tokenGasPriceFactor` parameter to a high value (e.g. `type(uint256).max`), which will significantly reduce the payment amount sent to `refundReceiver`. This is because `tokenGasPriceFactor` is the denominator in the division, and increasing its value will cause the result to approach zero.

2. A bad actor (possibly the `refundReceiver`) can modify the `tokenGasPriceFactor` parameter to a lower value to increase the payout. For example, if `tokenGasPriceFactor = 10`, the attacker could set `tokenGasPriceFactor = 1` to receive a 10x increase in the payout. This is because decreasing the `tokenGasPriceFactor` value will increase the result payment amount, since it is the dividing factor.

## PoC

This test illustrates the case of
SmartAccount implementation can be destroyed by a bad actor
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


# Vulnerability details

The SmartAccount wallet architecture is mainly defined by a `Proxy` contract that delegates functionality to a common reusable `SmartAccount` implementation.

The `SmartAccountFactory` contract has the address of a base `SmartAccount` implementation (immutable variable `_defaultImpl`). When a new wallet is created, the factory deploys a new `Proxy` that points its implementation to this base `SmartAccount` implementation, and then calls the `init` function to initialize the wallet (sets owner, sets entrypoint, etc.).

![architecture](https://i.ibb.co/1qLGwNV/biconomy-architecture-drawio.png)

Each Proxy contract that represents a wallet holds its storage state and delegates functionality to a `SmartAccount` implementation. This implementation contract is shared by all wallets.

Since the `SmartAccount` implementation is a contract that can be interacted with, a bad actor can initialize it and destroy this common implementation by:

1. Call `init` on the implementation contract to become the owner of the contract.
2. Using `execTransaction`, execute a `delegatecall` to a contract that performs the `selfdestruct` opcode.

## Impact

This security issue affects the functionality of existing wallets, rendering them unusable. The issue is caused by the destruction of the implementation contract code and the inability of the proxy to delegate functionality to the implementation contract. 

As a result, owners will lose access to their wallets and any associated funds, as well as any third party integrations that depend on the wallet. 

Additionally, the issue will prevent any upgrade functionality, as the upgrade logic is implemented in the same 'SmartAccount' contract using the 'Singleton' mixin and accessed through the 'updateImplementation' function. This means that upgrades will also be unavailable once the implementation contract is destroyed.

## PoC

The following test reproduces the issue. An attacker calls `init` on the implementation contract and executes a `delegatecall` to a simple contract that does the `selfdestruct`.

Note that in foundry tests, `selfdestruct` doesn't take effect until the test finalizes (see [here](https://github.com/foundry-rs/foundry/issues/1543)).

```solidity
contract Destroyable {
    function destroy() external {
        selfdestruct(payable(msg.sender));
    }
}

contract AuditTest is Test {
    bytes32 internal constant ACCOUNT_TX_TYPEHASH = 0xc2595443c361a1f264c73470b9410fd67ac953ebd1a3ae63a2f514f3f014cf07;

    uint256 bobPrivateKey = 0x123;
    uint256 attackerPrivateKey = 0x456;

    address deployer;
    address bob;
    address attacker;
    address entrypoint;
    address handler;

    SmartAccount public implementation;
    SmartAccountFactory public factory;
    MockToken public token;

    function setUp() public {
        deployer = ma