xanoutas/solidity-security-findings huggingface dataset:
Lack of validation of the `FixedTermLoanHooks.setFixedTermEndTime` function can lead to unintended behaviour
Audit: 2024-08-wildcat-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L188-L192
https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L238-L240


# Vulnerability details

## Proof of Concept

The `FixedTermLoanHooks.setFixedTermEndTime` function is used to update the `fixedTermEndTime` of the `HookedMarket`. The input validation of the `newFixedTermEndTime` value before the update is as follows:

```solidity
    if (!hookedMarket.isHooked) revert NotHookedMarket();
    if (newFixedTermEndTime > hookedMarket.fixedTermEndTime) revert IncreaseFixedTerm();
    hookedMarket.fixedTermEndTime = newFixedTermEndTime;
```

As it is evident from the above code snippet there is no check to ensure that `newFixedTermEndTime > block.timestamp`. But this is a neccesary check to ensure that the meaningful `fixedTerm` is set in the `HookedMarket` and that withdrawals can not be ` immediately queued` by the lender before the end of the `fixedTermEndTime`. This is evident in the logic how the `fixedTermEndTime` is validated in the `FixedTermLoanHooks._onCreateMarket` function as shown below:

```solidity
    if (
      fixedTermEndTime < block.timestamp || (fixedTermEndTime - block.timestamp) > MaximumLoanTerm
    ) { //@audit-info - MaximumLoanTerm = 365 days
      revert InvalidFixedTerm();
    }
```

As it is obvious from the above code snippet the `fixedTermEndTime < block.timestamp` check is performed to ensure that `fixedTermEndTime` is not less than the current timestamp.

Since the above check is missing in the `FixedTermLoanHooks.setFixedTermEndTime` function a `newFixedTermEndTime` could be set which is less than the current timestamp (block.timestamp). As a result the `lenders` can immediately `queue the withdrawals` which is not the intended behavior of the `fixedTerm hook contract`.

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L188-L192

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L238-L240

## Recommended Mitigation Steps

Hence it is recommended to update the `FixedTermLoanHooks.setFixedTermEndTime` function to include the following check.

```solidity
    if (newFixedTermEndTime < block.timestamp) revert InvalidFixedTerm();
```


## Assessed type

Invalid Validation
Memory Corruption Risk in LibStoredInitCode Due to Scratch Space Overuse
Audit: 2024-08-wildcat-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/libraries/LibStoredInitCode.sol#L60-L78


# Vulnerability details

## Vulnerability Description: 

In the function `calculateCreate2Address` within the `LibStoredInitCode` library, the `create2Prefix, salt, and initCodeHash` are written to memory addresses that fall within the `reserved scratch space` (specifically 0x00 to 0x3f), which the EVM typically uses for hashing operations such as keccak256. This creates a risk of memory corruption if the same memory range is used during the hash operation.

## Impact: 

Since the EVM uses the memory range 0x00 to 0x3f as scratch space during operations like keccak256, the current usage of these memory slots for storing the create2Prefix and salt may lead to memory overwriting or incorrect behavior when calculating the hash. This can result in an incorrect keccak256 hash, potentially breaking the contract logic and creating unexpected results.

## Proof of Concept (PoC): 

In the calculateCreate2Address function:

```solidity
assembly {
    mstore(0x00, create2Prefix)  // Store create2Prefix at 0x00
    mstore(0x20, salt)  // Store salt at 0x20
    mstore(0x40, initCodeHash)  // Store initCodeHash at 0x40 (outside the scratch space)
    create2Address := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
}
```

The issue arises because `mstore` uses memory addresses 0x00 and 0x20, which are within the EVM's reserved scratch space. When the keccak256 function is called, it may use the same scratch space, leading to a potential overwrite and hash corruption.

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/libraries/LibStoredInitCode.sol#L60-L78

## Recommended Fix: 

To avoid the risk of memory corruption, the storage locations for create2Prefix, salt, and initCodeHash should be shifted to a safer memory location outside the EVM’s reserved scratch space. For example, you can start storing the values at 0x60 to avoid interference with the EVM's scratch space:

```solidity
assembly {
    mstore(0x60, create2Prefix)  // Store create2Prefix at 0x60
    mstore(0x80, salt)  // Store salt at 0x80
    mstore(0xa0, initCodeHash)  // Store initCodeHash at 0xa0
    create2Address := and(keccak256(0x60, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
}
```

This ensures that the scratch space is preserved for EVM operations and avoids any potential conflicts. Once the operations are done you can reset original values on these memory slots (need to cache the original values before using them for this transaction).




## Assessed type

Other
Replay attack on UserOperation if the smart account is not deployed before
Audit: 2023-01-biconomy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L64


# Vulnerability details

## Impact

During handling an UserOperation, if the smart account is not deployed already, it will be created and then the UserOperation will be executed. The vulnerability is that since the smart account is going to be deployed before the execution of the UserOperation, it will bypass the nonce incrementation. This provides an opportunity to apply replay attack to execute the same UserOperation twice. 

Replaying a user's transaction can have critical financial impact on the user. For example, transferring fund from the user to the receiver twice instead of once.

## Proof of Concept

 - Suppose, Alice (a honest user) sends a UserOperation to be executed by the bundler for the first time (I mean the smart account for Alice is not created yet, so the nonce is zero). The gas of this operation will be refunded whether by Alice's smart account or a paymaster (it does not matter for this vulnerability).
 -The function `handleOps` will be called by the bundler to validate and execute them.
https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L68
 - During the account prepayment validation of UserOperation, the Alice's smart account will be deployed if its not created already.
https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L295
 - Since `initCode.length != 0`, the following function will deploy the Alice's smart account.
```
function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal {
        if (initCode.length != 0) {
            address sender = opInfo.mUserOp.sender;
            if (sender.code.length != 0) revert FailedOp(opIndex, address(0), "AA10 sender already constructed");
            address sender1 = senderCreator.createSender{gas : opInfo.mUserOp.verificationGasLimit}(initCode);
            if (sender1 == address(0)) revert FailedOp(opIndex, address(0), "AA13 initCode failed or OOG");
            if (sender1 != sender) revert FailedOp(opIndex, address(0), "AA14 initCode must return sender");
            if (sender1.code.length == 0) revert FailedOp(opIndex, address(0), "AA15 initCode must create sender");
            address factory = address(bytes20(initCode[0 : 20]));
            emit AccountDeployed(opInfo.userOpHash, sender, factory, opInfo.mUserOp.paymaster);
        }
    }
```
https://github.com/code-423n4/2023-01-biconomy/blob/5df2e8f8c0fd3393b9ecdad9ef356955f07fbbdd/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L261
 - After the deployment, the Alice's UserOperation should be validated.
https://github.com/code-423n4/2023-
`tokenGasPriceFactor` can be changed to either maximize gas refund or minimize it
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#L431-L443


# Vulnerability details

## Description
`tokenGasPriceFactor` is used to calculate how much tokens that are going to be returned to cover gas costs:

```javascript
File: SmartAccount.sol

263:        } else {
264:            payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);
265:            require(transferToken(gasToken, receiver, payment), "BSA012");
266:        }
```

However, it is not included in the data that is used for signature:

```javascript
File: SmartAccout.sol

424:    function encodeTransactionData(
425:        Transaction memory _tx,
426:        FeeRefund memory refundInfo,
427:        uint256 _nonce
428:    ) public view returns (bytes memory) {
429:        bytes32 safeTxHash =
430:            keccak256(
431:               abi.encode(
432:                    ACCOUNT_TX_TYPEHASH,
433:                    _tx.to,
434:                    _tx.value,
435:                    keccak256(_tx.data),
436:                    _tx.operation,
437:                    _tx.targetTxGas,
438:                    refundInfo.baseGas,
439:                    refundInfo.gasPrice,
440:                    refundInfo.gasToken,
441:                    refundInfo.refundReceiver, // no tokenGasPriceFactor
442:                    _nonce
443:                )
444:            );
445:        return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
446:    }
```

An attacker can get the `owner` to sign a message with one `tokenGasPriceFactor` and then submit another to the wallet, either to maximize or minimize gasPayment returned.

## Impact
`tokenGasPriceFactor` can be changed after signature to maximize or minimize tokens refunded for gas.

## Proof of Concept
Modified `can send transactions and charge wallet for fees in erc20 tokens` in `testGroup1.ts` to show that `tokenGasPriceFactor` can be modified after signature:

```javascript
  it("can send transactions and charge wallet for fees in erc20 tokens", async function () {
    await token
      .connect(accounts[0])
      .transfer(userSCW.address, ethers.utils.parseEther("100"));

    const tokenBalanceBefore = await token.balanceOf(bob);
    console.log("bob before",tokenBalanceBefore.toString());

    const safeTx: SafeTransaction = buildSafeTransaction({
      to: token.address,
      // value: ethers.utils.parseEther("1"),
      data: encodeTransfer(charlie, ethers.utils.parseEther("10").toString()),
      nonce: await userSCW.getNonce(0),
    });

    const gasEstimate1 = await ethers.provider.estimateGas({
      to: token.address,
      data: encodeTransfer(charlie, ethers.utils.parseEther("10").toString()),
      from: userSCW.address,
    });

    const chainId = await userSCW.getChainId();

    safeTx.refundReceiver = "0x0000000000000000000000000000000000000000";
    safeTx.gasToken = token.address;
    safeTx.
Owner could brick all proxies by overwriting the implementation contract and making `updateImplementation` unusable
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#L120-L125
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol#L120-L125


# Vulnerability details

## Impact

Usually, when implementing a proxy that delegates all the calls to an implementation contract, the proxy itself has a function that allows it to replace the implementation contract. 

This function ALWAYS is not inside the implementation contract, because if there is an update (perhaps via `create2`) with the implementation contract the wallet will be unusable and the fund will be lost forever.

## Proof of Concept
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L120-L125
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountNoAuth.sol#L120-L125

## Tools Used
Manual review

## Recommended Mitigation Steps
Move the `updateImplementation` from the implementation contract itself to the proxy contract.
A malicious user can make other's smart wallet invoke any external contract by deploying the smart wallet
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#L33
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L32


# Vulnerability details

## Impact

A malicious user can initiate an arbitrary external call in the role of other user's smart wallet, which is equivalent to stealing a smart wallet and sending a transaction with it.
This can lead to fund being stolen from the smart wallet directly, may also lead to authority, funds, function problems of other contracts.

## Proof of Concept

If a smart wallet has not been deployed or is being deployed, a hacker can launch an attack by deploying the wallet directly or front-running the deployment.

The following lists some possible attack scenarios.

If a hacker wants to steal USDT from a smart wallet:
1. The hacker invokes [SmartAccountFactory.sol#deployCounterFactualWallet](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33) to deploy the smart wallet contract and sets `_handler` to the USDT contract address.
2. The hacker invokes the `ERC20 - approve` function of the smart contract wallet.
3. The hacker invokes the `ERC20 - transferFrom` function of the USDT contract to steal all the USDT from the smart wallet.

The principle of step 2 is:
The smart contract wallet itself does not implement the `ERC20 - approve` function, so the invoking will go into [FallbackManager.sol#fallback](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L32).
Because in step 1, the hacker has already set the fallback handler to the USDT contract address when deploying the smart wallet, so eventually the smart wallet will call the `approve` function of USDT contract in [FallbackManager.sol#fallback](https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L32).

If a hacker wants to steal some ERC721 assets from a smart wallet:
Similarly, the hacker just need to set the fallback handler to the ERC721 contract address in step 1, and invokes `ERC721 - setApprovalForAll` or `ERC721 - transferFrom` in step 2.

If a smart wallet manages the assets or functions of another contract:
Similarly, the hacker just need to set the fallback handler to the target contract address in step 1, and invokes the specific function in step 2.

## Tools Used

Manual

## Recommended Mitigation Steps

I recommend removing th
Deploy wallet for another user with fake entityPoint or handler 
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#L33
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L34
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L43


# Vulnerability details

Links to affected code: https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33:45

***
## Vulnerability details:
### Short issue description: 
Since anyone can deploy SmartWalelt for anyone on another chain (for those who do not yet have a wallet) and can change the ```entryPoint``` and ```handler``` parameters during deployment, can access the funds of someone else's wallet. Because **Wallet addresses are counterfactual in nature (you can know the address in advance and users will have the same address across all EVM chains)** and somebody can transfer direct or by mistake, funds to not deployed wallet address. An attacker can deploy his own wallet to the user's wallet address and withdraw all funds, change of owner, impersonating someone else.

**This bug is identical to the hack that happened with Optimism.**

## Impact
- Loss of funds
- Change of owner
- Any actions with the wallet
- Everything that follows from the above points
***

## Tools Used
https://github.com/code-423n4/2023-01-biconomy/
hardhat
***
## Recommended Mitigation Steps
- Create whitelist for ```address _entryPoint```, ```address _handler``` or add this parameters to calculate of salt for deploy contract

## Proof of Concept
### Let's now analyze in detail why it's possible:
We have next code for deploy wallet:
```java
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;
}
```
### Has the following highlights:
- anyone can call `deployCounterFactualWallet`
- `_owner, _entryPoint, _handler, _index` transfer by parameters
- **ONLY** `_owner` and `_index` used in calculate salt
### How can this be used?:

We can deploy `Smart Wallet` for any user,
User pays more to unstake
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/aa-4337/core/StakeManager.sol#L48


# Vulnerability details

## Impact
In function:

        function depositTo(address account) public payable {
        internalIncrementDeposit(account, msg.value);
        DepositInfo storage info = deposits[account];
        emit Deposited(account, info.deposit);
        }

the parameters from info are not updated, just the mapping:
    mapping(address => DepositInfo) public deposits;

When you want to withdraw though, you need to have some parameters set

        function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external {
        DepositInfo storage info = deposits[msg.sender];
        require(withdrawAmount <= info.deposit, "Withdraw amount too large");
        info.deposit = uint112(info.deposit - withdrawAmount);
        emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount);
        (bool success,) = withdrawAddress.call{value : withdrawAmount}("");
        require(success, "failed to withdraw");
        }

As you can see you need that         require(withdrawAmount <= info.deposit, "Withdraw amount too large");   to withdraw, and this struct it is not updated when depositing

The only way to update it is staking, so eventually deposits will be lost because you can't update the mapping other way

        function addStake(uint32 _unstakeDelaySec) public payable {
        DepositInfo storage info = deposits[msg.sender];
        require(_unstakeDelaySec > 0, "must specify unstake delay");
        require(_unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time");
        uint256 stake = info.stake + msg.value;
        require(stake > 0, "no stake specified");
        require(stake < type(uint112).max, "stake overflow");
        deposits[msg.sender] = DepositInfo(
            info.deposit,
            true,
            uint112(stake),
            _unstakeDelaySec,
            0
        );
        emit StakeLocked(msg.sender, stake, _unstakeDelaySec);
    }


## Tools Used
hand
## Recommended Mitigation Steps
Update the struct to a deposit value when depositing, otherwise you won't be able to withdraw
Wrong verification of isContract
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/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L49


# Vulnerability details

## Impact
Bypass security concerns from the protocol in terms of using smart contracts to interact with them

## Proof of Concept

As you can see, the isContract validation will still return false or 0 if the caller is a smart that does not have code after the constructor. This happens because the opcode extcodesize works in a way where it will look up the contract address in the Ethereum state, and retrieve the size of the code stored at that address. If you have the logic in an attacker contract calling the depositFor function from the constructor, this require statement will pass and the contract will be able to deposit

Attacker contract:

        constructor(biconomy _bic) {
        Biconomy= _bic;
        //isContract fails and returns false. 
        isContract = Address.isContract(address(this));
        addr = address(this);
        // This will work
        Biconomy.depositFor(address(this)); //parameter is not important
    
        }

## Tools Used
Hand

## Recommended Mitigation Steps

      require(msg.sender == tx.origin)

It has its down sides but I think it is a tool alternative here

## references

Open Zeppelin Documentation:
@dev Returns true if account is a contract. *
[IMPORTANT]
====
It is unsafe to assume that an address for which this function returns
false is an externally-owned account (EOA) and not a contract.
Among others, isContract will return false for the following
types of addresses:
an externally-owned account
a contract in construction
an address where a contract will be created
an address where a contract lived, but was destroyed
====
*/