xanoutas/solidity-security-findings huggingface dataset:
User can drain the paymaster (MagicSend) deposit quickly because lack of validation from field mUserOp.verificationGasLimit
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

# User can drain the paymaster (MagicSend) deposit quickly because lack of validation from field mUserOp.verificationGasLimit

### Line of code
1. https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L518 
2. https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L325
3. https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L451
4. https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L567
5. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L143
6. https://eips.ethereum.org/EIPS/eip-4337
### Proof of concept

I would like to provide some context to go over how the logic works in the entryPoint side.

[Line of code](https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L518)
```solidity
(uint256 requiredPreFund) = _getRequiredPrefund(mUserOp);
        (gasUsedByValidateAccountPrepayment, validationData) = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund);

        if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
            revert FailedOp(opIndex, "AA25 invalid account nonce");
        }

        //a "marker" where account opcode validation is done and paymaster opcode validation is about to start
        // (used only by off-chain simulateValidation)
        numberMarker();

        bytes memory context;
        if (mUserOp.paymaster != address(0)) {
            (context, paymasterValidationData) = _validatePaymasterPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, gasUsedByValidateAccountPrepayment);
```
The MagicSend contract aims to be pay master complicant.

first, we compute how much fund is required for paymaster to pay for the transaction:
```solidity
_getRequiredPrefund(mUserOp);
```
[Link of code](https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L325)
```solidity
 function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal pure returns (uint256 requiredPrefund) {
    unchecked {
        //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call.
        // our security model might call postOp eventually twice
        uint256 mul = mUserOp.paymaster != address(0) ? 3 : 1;
        uint256 requiredGas = mUserOp.callGasLimit + mUserOp.verificationGasLimit * mul + mUserOp.preVerificationGas;

        requiredPrefund = requiredGas * mUserOp.maxFeePerGas;
    }
    }
```
As we can see, user can set `maxFeePerGas` very high, then `requiredPrefund` will be high

[Line of code](https://etherscan.io/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code#F2#L451)

When calling `_validatePaymasterPrepayment`,
```solidity
paymasterInfo.deposit = uint112(deposit -
Should not allow replaying the remove owner signature
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

# Should not allow replaying the remove owner signature

### Line of code
1. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137
2. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L180
### Proof of concept
In Smart wallet,

[Line of code](https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137)
```solidity
 function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
        public
        payable
        virtual
        onlyEntryPoint
        payPrefund(missingAccountFunds)
        returns (uint256 validationData)
    {
        uint256 key = userOp.nonce >> 64;
        // 0xbf6ba1fc = bytes4(keccak256("executeWithoutChainIdValidation(bytes)"))
        if (userOp.callData.length >= 4 && bytes4(userOp.callData[0:4]) == 0xbf6ba1fc) {
            userOpHash = getUserOpHashWithoutChainId(userOp);
            if (key != REPLAYABLE_NONCE_KEY) {
                revert InvalidNonceKey(key);
            }
        } else {
            if (key == REPLAYABLE_NONCE_KEY) {
                revert InvalidNonceKey(key);
            }
        }
```
as we can see:
```solidity
// 0xbf6ba1fc = bytes4(keccak256("executeWithoutChainIdValidation(bytes)"))
        if (userOp.callData.length >= 4 && bytes4(userOp.callData[0:4]) == 0xbf6ba1fc) {
            userOpHash = getUserOpHashWithoutChainId(userOp);
            if (key != REPLAYABLE_NONCE_KEY) {
                revert InvalidNonceKey(key);
            }
        }
```
if we are calling `executeWithoutChainIdValidation`,

the nonce is a fixed value `REPLAYABLE_NONCE_KEY`

[Line of code](https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L180)
```solidity
function executeWithoutChainIdValidation(bytes calldata data) public payable virtual onlyEntryPoint {
        bytes4 selector = bytes4(data[0:4]);
        if (!canSkipChainIdValidation(selector)) {
            revert SelectorNotAllowed(selector);
        }

        _call(address(this), 0, data);
    }

```
and we call:
```solidity
function canSkipChainIdValidation(bytes4 functionSelector) public pure returns (bool) {
        if (
            functionSelector == MultiOwnable.addOwnerPublicKey.selector
                || functionSelector == MultiOwnable.addOwnerAddress.selector
                || functionSelector == MultiOwnable.removeOwnerAtIndex.selector
                || functionSe
ETH in MagicSpend.sol can be stolen by passing high gas fee
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

# ETH in MagicSpend.sol can be stolen by passing high gas fee

### Line of code
1. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L109
2. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L114

### Proof of concept
The `MagicSpend.sol` is aims to be paymaster complicant:

[Line of code](https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L109)
```solidity
function validatePaymasterUserOp(UserOperation calldata userOp, bytes32, uint256 maxCost)
        external
        onlyEntryPoint
        returns (bytes memory context, uint256 validationData)
    {
```
and the full implementation is:

[Line of code](https://github.com/code-423n4/2024-03coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L114)
```solidity
        WithdrawRequest memory withdrawRequest = abi.decode(userOp.paymasterAndData[20:], (WithdrawRequest));
        uint256 withdrawAmount = withdrawRequest.amount;

        if (withdrawAmount < maxCost) {
            revert RequestLessThanGasMaxCost(withdrawAmount, maxCost);
        }

        if (withdrawRequest.asset != address(0)) {
            revert UnsupportedPaymasterAsset(withdrawRequest.asset);
        }

        _validateRequest(userOp.sender, withdrawRequest);

        bool sigFailed = !isValidWithdrawSignature(userOp.sender, withdrawRequest);
        validationData = (sigFailed ? 1 : 0) | (uint256(withdrawRequest.expiry) << 160);

        // Ensure at validation that the contract has enough balance to cover the requested funds.
        // NOTE: This check is necessary to enforce that the contract will be able to transfer the remaining funds
        //       when `postOp()` is called back after the `UserOperation` has been executed.
        if (address(this).balance < withdrawAmount) {
            revert InsufficientBalance(withdrawAmount, address(this).balance);
        }

        // NOTE: Do not include the gas part in withdrawable funds as it will be handled in `postOp()`.
        _withdrawableETH[userOp.sender] += withdrawAmount - maxCost;
        context = abi.encode(maxCost, userOp.sender);
```
note the check:
```solidity
// Ensure at validation that the contract has enough balance to cover the requested funds.
// NOTE: This check is necessary to enforce that the contract will be able to transfer the remaining funds
//       when `postOp()` is called back after the `UserOperation` has been executed.
if (address(this).balance < withdrawAmount) {
    revert InsufficientBalance(withdra
Missing Nonce and Expiry Validation in Signature Verification
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L260-L268
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L184
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L127


# Vulnerability details

## Impact
The vulnerability in the `isValidWithdrawSignature` function, due to its inadequate validation of nonce and expiry time, can lead to unauthorized fund withdrawals or unauthorized operations being processed in functions such as `validatePaymasterUserOp` and `withdraw`. This poses a significant risk to the security and integrity of the `MagicSpend` contract, potentially resulting in financial losses or exploitation by attackers.
## Proof of Concept
The vulnerability lies in the `isValidWithdrawSignature` function within the provided Solidity contract. This function is responsible for verifying the validity of a signature provided by a user for a withdrawal request. However, it fails to adequately validate the nonce and expiry time associated with the withdrawal request, which can lead to potential exploits.

```solidity
function isValidWithdrawSignature(address account, WithdrawRequest memory withdrawRequest)
        public
        view
        returns (bool)
    {
        return SignatureCheckerLib.isValidSignatureNow(
            owner(), getHash(account, withdrawRequest), withdrawRequest.signature
        );
    }
```

The vulnerability in the `isValidWithdrawSignature` function affects other functions within the `MagicSpend` contract, such as `validatePaymasterUserOp` and `withdraw`. Here's a detailed explanation:
1. `isValidWithdrawSignature` Function:
This function is responsible for validating the signature provided by a user for a withdrawal request. However, it fails to adequately validate the nonce and expiry time associated with the withdrawal request, leaving the contract vulnerable to exploitation.
2. Impact on `validatePaymasterUserOp` Function:
- The `validatePaymasterUserOp` function utilizes `isValidWithdrawSignature` to validate withdrawal requests before processing user operations. Since `isValidWithdrawSignature` does not properly check the nonce and expiry time, it can lead to vulnerabilities in `validatePaymasterUserOp`.
 - Without proper validation of nonce and expiry time, an attacker could submit a withdrawal request with a reused nonce or an expired expiry time. This could trick the `validatePaymasterUserOp` function into processing unauthorized or outdated withdrawal requests, leading to potential fund losses or unauthorized operations.
3. Impact on `withdraw` Function:
- Similarly, the `withdraw` function relies on `isValidWithdrawSignature` to validate withdrawal requests before processing fund withdrawals. The lack of proper nonce and expiry time validation in `isValidWithdrawSignatur
Insecure Withdrawal Function Allows Unauthorized Access to Funds
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The lack of validation in the `withdrawGasExcess` function exposes the contract to the risk of unauthorized fund withdrawals. Malicious actors could exploit this vulnerability to drain the contract of its funds, leading to financial loss or disruption of intended contract functionalities.


## Proof of Concept
The `withdrawGasExcess` function in the `MagicSpend` contract facilitates the withdrawal of excess ETH associated with a user's account. However, the function lacks proper validation before executing the withdrawal. The function directly calls the internal `_withdraw` function without performing any validation checks. This internal function is documented with a comment stating that callers must validate the withdrawal legitimacy before calling it, implying that the responsibility for validation lies outside of the function.
```solidity
function withdrawGasExcess() external {
    uint256 amount = _withdrawableETH[msg.sender];
    // we could allow 0 value transfers, but prefer to be explicit
    if (amount == 0) revert NoExcess();

    delete _withdrawableETH[msg.sender];
    _withdraw(address(0), msg.sender, amount);
}
```
The absence of validation in the `withdrawGasExcess` function means that any user can invoke it to withdraw funds without proper authorization or validation. This could potentially lead to unauthorized fund withdrawals, bypassing any intended restrictions or security measures.


## Tools Used
Manual
## Recommended Mitigation Steps
Proper validation should be added to the `withdrawGasExcess` function to ensure that only legitimate withdrawals are allowed. This can be achieved by performing checks to verify the legitimacy of the withdrawal request, such as ensuring that the withdrawal amount is valid and that the caller is authorized to withdraw funds. 





## Assessed type

Access Control
Arithmetic Overflow and Underflow in FCL Contract
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/FreshCryptoLib/FCL.sol#L82


# Vulnerability details

## Impact
The unchecked block in the ecAff_isOnCurve function could potentially lead to arithmetic overflow or underflow. This could result in unexpected behavior and incorrect results, which could compromise the integrity of the elliptic curve checks.
## Proof of Concept
```solidity
 function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
        if (((0 == x) && (0 == y)) || x == p || y == p) {
            return false;
        }
>>>        unchecked {
            uint256 LHS = mulmod(y, y, p); // y^2
            uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax
>>>            RHS = addmod(RHS, b, p); // x^3 + a*x + b

            return LHS == RHS;
        }
    }
```
The code above shows how ecAff_isOnCurve(...) function is implemented in the FCL contract, as pointed out from the pointer, unchecked keyword is used, which means that arithmetic operations in the block will not check for underflow or overflow.
The would open up the risk of wrong value implementation in the contract. 
Note: There are other instances of this vulnerability in the contract which would allow this wrong value usage
## Tools Used
Manual Review
## Recommended Mitigation Steps
Protocol should consider using a Math library for safe arithmetic operations to avoid Overflow or underflow


## Assessed type

Under/Overflow
using deleted mapping
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
In the scenario 'Pay gas and transfer funds during execution' under `7.` `withdrawGasExcess()` is specified . `withdrawGasExcess` is called before `postOp`(10.). 
When `withdrawGasExcess` is executed, `_withdrawableEth[msg.sender]` is deleted.
In `postOp`, to calculate `withdrawable`, use `_withdrawableEth[account]', which was deleted in `withdrawGasExcess`.
In such a scenario, an error may occur in the contract.
🔗Link to the diagram: https://github.com/coinbase/magic-spend?tab=readme-ov-file#pay-gas-and-transfer-funds-during-execution

## Proof of Concept
```     
function withdrawGasExcess() external {
       ...
        delete _withdrawableETH[msg.sender];
        _withdraw(address(0), msg.sender, amount);
    }  
```


```
    function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost)
        external
        onlyEntryPoint
    {
        ...
        // Compute the total remaining funds available for the user accout.
        // NOTE: Take into account the user operation gas that was not consummed.
        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);
        ...     
        }
    }
```

## Tools Used
Manual

## Recommended Mitigation Steps
reset to zero `_withdrawableETH[account]` instead of deleting


## Assessed type

Other
Incorrect count of attributes in postOp function
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/MagicSpend/MagicSpend.sol#L143
https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L705-L708


# Vulnerability details

## Impact
`EntryPoint` will be unable to execute the `postOp` callback to the paymaster.

## Proof of Concept
`EntryPoint` contract performs the `postOp` callback with four parameters - `(mode, context, actualGasCost, gasPrice)`.
```solidity
    function _postExecution(
        IPaymaster.PostOpMode mode,
        UserOpInfo memory opInfo,
        bytes memory context,
        uint256 actualGas
    ) private returns (uint256 actualGasCost) {
        uint256 preGas = gasleft();
        unchecked {
            address refundAddress;
            MemoryUserOp memory mUserOp = opInfo.mUserOp;
            uint256 gasPrice = getUserOpGasPrice(mUserOp);

            address paymaster = mUserOp.paymaster;
            if (paymaster == address(0)) {
                refundAddress = mUserOp.sender;
            } else {
                refundAddress = paymaster;
                if (context.length > 0) {
                    actualGasCost = actualGas * gasPrice;
                    if (mode != IPaymaster.PostOpMode.postOpReverted) {
                        try IPaymaster(paymaster).postOp{
                            gas: mUserOp.paymasterPostOpGasLimit
>>                      }(mode, context, actualGasCost, gasPrice)
                        ...

```
However, the current implementation of `MagicSpend.sol` uses the `postOp` function with only three attributes.
```solidity
    function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost)
        external
        onlyEntryPoint
```
Due to this discrepancy, attempts to call the `postOp` function from the `EntryPoint` contract will revert.
## Tools Used
Manual review
## Recommended Mitigation Steps
```diff
+   function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint256 /*gasPrice*/)
        external
        onlyEntryPoint
```



## Assessed type

DoS
Trust issue with ownership of the contract admin
Audit: 2024-03-coinbase-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
In the protocol there is a priviliged `owner` account which is set to be initialized as a contract address or a `EOA` ` if (isOwnerAddress(msg.sender) || (msg.sender == address(this)))`. So if anyone of these is right it will get executed, that means a `eoa` can also be set .

## Proof of Concept
```
    function _checkOwner() internal view virtual {
        if (isOwnerAddress(msg.sender) || (msg.sender == address(this))) {
            return;
        }

        revert Unauthorized();
    }
```
So when the contract is deployed a `EOA` is set to be owner of the account which it will be during deployment process it can stop various operation in the protocol , infact the most important of them can be `adding owners` to the protocol
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L232
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/MultiOwnable.sol#L93
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/MultiOwnable.sol#L102

Some if the effected protocol based features will be -
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L330
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L212
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L222
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L232

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Review the proper admin related logic & maybe remove the onlyOwner logic from the functions like `_addOwners` for appropriate DAO-governance logic


## Assessed type

Access Control