xanoutas/solidity-security-findings huggingface dataset:
Submission Edit: Maliciously crafted UserOperation can revert entire bundle txs to grief protocol, expend bundler funds, and cause bundler or MagicSpend reputation
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L587


# Vulnerability details

# Edit only to provide missing internal function implementations within the test file PoC provided to the original submission:

## I belatedly realized my previous submission of another DOS finding did not include the implementations of environment test config functions `_initializeSmartWallet(validUser, validUserAccount)` and `_getValidUserOpSigned('')`- they are included in the file below for use in running my previous PoC, simply add to the bottom of the previously provided PoC test contract file.

```solidity
function _initializeSmartWallet(address owner, CoinbaseSmartWallet target) internal {
        delete owners;
        owners.push(abi.encode(owner));
        target.initialize(owners);
    }

    function _getValidUserOpSigned(bytes memory _paymasterAndData) internal view returns (UserOperation memory validOp, bytes32 retOpHash) {
        // populate valid userOp fields using storage struct
        validOp = userOp;
        validOp.sender = address(validUserAccount);
        validOp.paymasterAndData = _paymasterAndData;

        // populate valid userOp signature
        retOpHash = entryPoint.getUserOpHash(validOp);
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(validUserPrivateKey, retOpHash);
        bytes memory sig = abi.encodePacked(r, s, v);
        validOp.signature = abi.encode(CoinbaseSmartWallet.SignatureWrapper(0, sig));
    }
```
```


## Assessed type

DoS
Draining `MagicSpend.balances` with spam operations
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L196


# Vulnerability details

## Impact
loss in funds for  `CoinbaseSmartWallet.execute()` or `CoinbaseSmartWallet.executeBatch()`


## Proof of Concept
Currently, users are not charged for their operations

Gas compensations are taken from `MagicSpend.balances[account]`, where account is the destination address in `CoinbaseSmartWallet.execute()` or `CoinbaseSmartWallet.executeBatch()`.
These accounts are charged with their `MagicSpend.balances[]`.
As a result, users can spam multiple UserOps, and gas costs will be compensated from these account balances. This attack has no cost, but results in losses for account if actively used

## Tools Used
manual , fuzz testing

## Recommended Mitigation Steps
The key problem here is that the attack has no cost, and the `UserOp` does not pay for operations. We recommend charging `UserOp` and introducing some cost for potential attackers


## Assessed type

Error
The `MagicSpend` owner should be restricted to withdraw
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L161
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L169
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L181


# Vulnerability details

## Impact
It means that some of the `MagicSpend` accounts will not be able to withdraw
balances does not change 

## Proof of Concept

The owner accumulates ethCost on every `MagicSpend` usage. If the owner withdraws using `MagicSpend`.withdrawTokensTo(), some other of the SponsorPaymaster depositors will have less funds actually stored at Entrypoint.
As a result, operations will fail - having a zero balance on EntryPoint, but a non-zero balance on `MagicSpend`.

## Tools Used
manual

## Recommended Mitigation Steps
check balance after eth transfers


## Assessed type

ETH-Transfer
Malicious bundler can steal funds from `MagicSpend`
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L143


# Vulnerability details

## Impact
A hacker can drain the funds in just one transaction. This can be achieved by executing a userOp with an extremely high gas parameter

## Proof of Concept
a hacker sets preVerificationGas value to 99 ETH. This value is deducted from the `MagicSpend` and sent to the hacker who runs `handleOps()`.

## Tools Used
manual , foundry 

## Recommended Mitigation Steps
 1. Almost all gas parameters in userOp allow a hacker to set an arbitrarily high reward for themselves when calling entryPoint.handleOps()
 
2. add a global rate limit and a contract specific limit

3. Verification of gas settings in `validateUserOp` to prevent malicious 
 bundlers  from draining the Paymaster

4.  The architecture to charge compensation for executing userOp not from sponsors, but directly from the users. This implies deducting other types of cryptocurrencies that users hold in their wallets, such as USDT, for instance.

5. Restrict all gas parameters in `userOp` so that running `entryPoint.handleOps()` becomes unprofitable. Doing this reliably is challenging


## Assessed type

ETH-Transfer
Bypassing Wallet
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L291


# Vulnerability details

## Impact
`CoinbaseSmartWallet` can have from one to three owners and have different signer policies.  `CoinbaseSmartWallet` allows setting up a Wallet in such a way that it can be used by a user without a ID.

## Proof of Concept

To do this, a hacker first obtains an ID and Wallet. This is done either through a fake or stolen ID,or by stealing someone else's private key.   

the Wallet is in no way tied to the original account that created it, and the revocation or invalidation of the original owner's ID does not affect it.

The hacker can use the `CoinbaseSmartWallet`, as they own 2/3 of the signatures, and the `_validateSignature()`
function checks the KYC of only the first owner


## Tools Used
manual 

## Recommended Mitigation Steps
We recommend that in the `_validateSignature()` function, instead of checking the KYC of the first owner, each signature be simply counted valid only if its owner had a ID. Add a recovery functionality


## Assessed type

Error
Maliciously crafted UserOperation can revert entire bundle txs to grief protocol, expend bundler funds, and cause bundler or MagicSpend reputation
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L587
https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L60-L82
https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L108


# Vulnerability details

## Impact
By leveraging the `IEntryPoint.FailedOp` AA51 revert, a user can craft malicious calldata which reverts the entire bundle transaction of user operations provided to `EntryPoint.handleOps()`, effectively griefing protocol actors including other users, draining bundler fund balance, and cause bundler/paymaster reputation damage.

The first call of `EntryPoint._handlePostOp()` within `EntryPoint.innerHandleOp()` will trigger a second invocation of `_handlePostOp()` in the outer catch block which likewise reverts again with AA51 and bubbles the revert to the top level, reverting the entire bundle.

## Proof of Concept
The following test file is designed for integration with the audit repository by being placed in a new "PoC" or other name dir within `2024-03-coinbase/test/SmartWallet/` and then run to observe the ability to deterministically grief attack by overloading insufficient prefund. This type of payload can be used to target a bundler and expend their balance:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Test, console2} from "forge-std/Test.sol";
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol";
import {IStakeManager} from "account-abstraction/interfaces/IStakeManager.sol";
import {calldataKeccak} from "account-abstraction/core/Helpers.sol";
import {MockEntryPoint} from "../mocks/MockEntryPoint.sol";
import "test/MagicSpend/PaymasterMagicSpendBase.sol";
import "../CoinbaseSmartWallet/SmartWalletTestBase.sol";

contract PoC is Test {
    /// forge-config: default.fuzz.runs = 10_000_000

    uint256 msOwnerPrivateKey = 0xdeadbeef;
    address msOwner = vm.addr(msOwnerPrivateKey);
    MagicSpend magicSpend = new MagicSpend(msOwner);
    CoinbaseSmartWallet account = new MockCoinbaseSmartWallet();
    CoinbaseSmartWallet validUserAccount = new MockCoinbaseSmartWallet();
    IEntryPoint entryPoint = IEntryPoint(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789);

    // userop signature params
    uint256 signerPrivateKey = 0xa11ce;
    address signer = vm.addr(signerPrivateKey);
    uint256 validUserPrivateKey = 0xbeef;
    address validUser = vm.addr(validUserPrivateKey);
    address bundler = vm.addr(0xc0ffeebabe);

    // userop
    uint256 callGasLimit = 49152;
    uint256 verificationGasLimit = 378989;
    uint256 preVerificationGas = 273196043;
    uint256 maxFeePerGas = 1000304;
    uint256 maxPriorityFeePerGas = 1000000;
    
    // withdra
lack of access control in the initialize function
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src%2FSmartWallet%2FCoinbaseSmartWallet.sol#L114


# Vulnerability details

The `initialize` function can be called by anyone before it is initialized because the function lacks access control.
This will allow anyone who calls the function first to register any `owner` to the `OwnerIndex` .

```solidity
function initialize(bytes[] calldata owners) public payable virtual {
        if (nextOwnerIndex() != 0) {
            revert Initialized();
        }

        _initializeOwners(owners);
    }
```
The function is called after deployment to initialize the `Owners` which then gives the owners certain privileged controls. The function being unguarded allows anyone who calls the `initialize` function first to set the owners of the account.

## Impact
Account can be stolen if the `initialize` function gets called before the rightfull owner does.

## Proof of Concept


## Tools Used
Manual Review 
## Recommended Mitigation Steps
Recommended to add an `onlyInitilaizer` modifier to ensure the function is called by the right entity.


## Assessed type

Access Control
Missing Receiver Functions in `CoinbaseSmartWallet` Pose NFT Incompatibility and Lockup Risks
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/27873bc8881bc79ea21d1868cb13b58ab58ffdbd/src/SmartWallet/CoinbaseSmartWallet.sol#L1-L336


# Vulnerability details

## Impact

The ERC-721, and ERC-1155 are the most commonly used Ethereum token standards. Unlike EOAs (Externally Owned Accounts), smart wallets like the `CoinbaseSmartWallet` consist of code that must be explicitly programmed to handle tokens according to these standards. For ERC-721 and ERC-1155 tokens, this includes implementing functions like `onERC721Received` and `onERC1155Received`. Without these functions, NFTs received by the smart wallet can become stuck, as the wallet lacks the necessary code to move or trade them. This distinction from EOAs is crucial because a smart wallet's codebase can lead to problems if it does not comply with the ERC-721 and ERC-1155 standards by including the `checkOnReceived` functions. [RareSkill Article](https://www.rareskills.io/post/erc721#viewer-cucj0)

## Proof of Concept
A user inadvertently transfers an NFT to a `CoinbaseSmartWallet` using a non-safe transfer method.

User owns an NFT from an ERC-721 or ERC-1155 contract that allows non-safe transfers.

User calls `transferFrom (ERC-721)` or `safeTransferFrom` without safety checks (ERC-1155) to transfer the NFT to the `CoinbaseSmartWallet`.

The NFT contract transfers the NFT without calling the receiver functions since they are not required for non-safe transfers. 

The wallet contract receives the NFT, but since the `CoinbaseSmartWallet` does not have the `onERC721Received` or `onERC1155Received` functions implemented, it cannot acknowledge the receipt or interact with the NFT.

The NFT is now held by the wallet contract but is effectively stuck because the wallet lacks the necessary functions to execute further operations on the NFT, such as transferring it to another address.
Example:
```javascript
// Assume NFTContract is an ERC-721 contract with non-safe transfer function
NFTContract.transferFrom(userAddress, walletAddress, tokenId);
// NFT is transferred to walletAddress, but the wallet cannot manage the NFT

// Assume NFTContract is an ERC-1155 contract with non-safe batch transfer function
NFTContract.safeBatchTransferFrom(userAddress, walletAddress, [tokenId1, tokenId2], [amount1, amount2], data);
// NFTs are transferred to walletAddress, but the wallet cannot manage the NFTs
```

The lack of `receiver` functions in the `CoinbaseSmartWallet` leads to different outcomes: a reverted transfer when using safe methods, and stuck NFTs when using non-safe methods.

If an NFT contracts implements the `Check for onERC721Received` function and the `CoinbaseSmartWallet` lacks the corresponding receiver function, any transfer or minting attempt to the `CoinbaseSmartWallet` will `revert`, rendering the wallet incompatible with such NFT contracts. Users will be unable to receive or mint NFTs in this wallet without the necessary receiver implementations.

## Tools Used
Manua
Users making specific chain account ownership upgrades will likely cause issues when later using cross-chain replay-able ownership upgrades
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/MultiOwnable.sol#L102


# Vulnerability details

**Impact**

Users are able to upgrade their account's owners via either directly onto the contract with a regular transaction or via an ERC-4337 EntryPoint transaction calling `executeWithoutChainIdValidation`. If a user chooses to use a combination of these methods it's very likely that the addresses at a particular ownership index differ across chain. Therefore if a user later calls `removeOwnerAtIndex` on another chain will end up removing different addresses on different chains. It is unlikely this would be the users intention. The severity of this ranges from minimal (the user can just add the mistakenly removed owner back) or criticial (the user mistakenly removes their only accessible owner on a specific chain, permanently locking the account).

**Proof Of Concept**

Scenario A: Alice permanently bricks her account on an unused chain:
1. Alice has a CoinbaseSmartWallet, and uses it on Base, Ethereum & Optimism.
2. Alice later decides to add a new owner using a cross-chain `executeWithoutChainIdValidation`
3. Alice later wants to remove the initial owner (index 0) and does so by signing another cross-chain replayable signature.
4. Despite it not being her intention anyone could take that signature and replay it on Arbitrum, Avalanche etc as there is no check to stop the user removing the final owner.

Scenario A: Alice adds owners using both methods and ends up with undesired results
1. Alice has a CoinbaseSmartWallet, and uses across all chains.
2. She has Gnosis Safe's and ERC-6551 token bound accounts on different chains so adds them as owners on those specific chains using `execute`.
3. She then adds a secondary EOA address on all chains using `executeWithoutChainIdValidation`
4. Now if she uses `executeWithoutChainIdValidation` to call `removeOwnerAtInde` she will be removing different owners on different chains, which is likely not her intention.

While more complex scenarios than this might sound bizarre it's important to remember that Alice could be using this smart account for the next N years, only making changes sporadically, and as her ownership mappings across different chains become more out of sync the likelihood of a signifanct error occuring increases.

**Recommended Mitigation**
As `MultiOwnableStorage` uses a mapping to track owner information rather than a conventional array, it might be simpler to do away with the indexes entirely and have a `removeOwner(bytes calldata _ownerToRemove)` function. This would avoid the sitations outlined above where when calling `removeOwnerAtIndex` removes different owners on different chains. To ensure replayability and avoid having a stuck nonce on chains where `_ownerToRemove` is not an owner the function should not revert in the case the owner is not there, but instead return a bool `removed` to indicate whether an owner was removed or not.

This w
Wrong calculation of fee that users should pay in postOp of MagicSpend
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L156


# Vulnerability details

## Impact
Users pay must less fee than they should for using `maxGasCost - actualGasCost` as the userOp fee.
```solidity
        // https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L156
        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);
```

## Proof of Concept
1. `maxGasCost - actualGasCost` is used as the userOp fee.
```solidity
        // https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L156
        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);
```
2. The meaning of `actualGasCost`
* `actualGasCost` is passed through `postOp` [method](https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L143).
* According to [IPaymaster of ERC-4337](https://github.com/eth-infinitism/account-abstraction/blob/abff2aca61a8f0934e533d0d352978055fddbd96/contracts/interfaces/IPaymaster.sol#L48), `actualGasCost` is actual gas cost so far (without this postOp call)
* In other word, `actualGasCost=gasCostBeforePostOp`.
3. The cost of `postOp` has been ignored. So, MagicSpend refunds more than it should.

## Recommended Mitigation Steps
Add postOp cost to the userOp fee.
```
uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - (actualGasCost+ COST_OF_POST * gasPricePostOp));
```





## Assessed type

ETH-Transfer
`CoinbaseSmartWallet` can be drained by replaying `UserOperation` in case of PassKeySignatures
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/CoinbaseSmartWallet.sol#L137-L168
https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/CoinbaseSmartWallet.sol#L316-L321


# Vulnerability details

## Impact

The vulnerability within the `CoinbaseSmartWallet` contract arises from the ability to replay `UserOperation` (userOp) through the `entryPoint`'s `handleOps` method in case of PasskeyBased signatures, which can be invoked by any external actor/bundler. 

This issue is critically exacerbated by the design that allows `validateUserOp` to be called by the `entryPoint`, and gas costs for processing these operations are deducted from the wallet and reimbursed to the bundler or beneficiary, the attacker incurs no cost while potentially draining the wallet's funds through repeated submissions of the same operation.

## Proof of Concept

The provided test, `testPasskeyUserOPReplay,` clearly demonstrates this vulnerability by replaying a `UserOperation` for different users. This shows that the smart contract system does not prevent the same operation from being processed multiple times. The test's log outputs indicate the vulnerability, showing balance deductions after each replay. 

Furthermore, the scenario underscores a realistic exploit pathway: an attacker captures a legitimate transaction and repeatedly submits it to the system, draining funds from the `CoinbaseSmartWallet` without bearing gas costs.

<details>
<summary>Proof Of Code</summary>
Place this into the `ValidateUserOp.t.sol` file.

```javascript

function testPasskeyUserOPReplay() public {
    bytes32 hash = 0x15fa6f8c855db1dccbb8a42eef3a7b83f11d29758e84aed37312527165d5eec5;
    bytes32 challenge = account.replaySafeHash(hash);
    WebAuthnInfo memory webAuthn = Utils.getWebAuthnStruct(challenge);

    (bytes32 r, bytes32 s) = vm.signP256(passkeyPrivateKey, webAuthn.messageHash);
    bytes memory sig = abi.encode(
        CoinbaseSmartWallet.SignatureWrapper({
            ownerIndex: 1,
            signatureData: abi.encode(
                WebAuthn.WebAuthnAuth({
                    authenticatorData: webAuthn.authenticatorData,
                    clientDataJSON: webAuthn.clientDataJSON,
                    typeIndex: 1,
                    challengeIndex: 23,
                    r: uint256(r),
                    s: uint256(s)
                })
                )
        })
    );


    UserOperation memory userOp = _getUserOp();
    userOp.signature = sig;
    vm.deal(address(account), 1 ether);
    assertEq(address(account).balance, 1 ether);
    vm.etch(account.entryPoint(), address(new MockEntryPoint()).code);
    MockEntryPoint ep = MockEntryPoint(payable(account.entryPoint()));

    address user1 = makeAddr("user1");
    vm.deal(user1, 1 ether);
    address user2 = makeAddr("user2");
    vm.deal(user2, 1 ether);

    uint256 missingAccountFunds = 456;

    // make a call entryPoint with user1 and above userOp
    vm