xanoutas/solidity-security-findings huggingface dataset:
the nonce value is not increasing everytime
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/ExecutorPlugin.sol#L131


# Vulnerability details

## Impact
the nonce value is not increasing everytime

The nonce value is used to create the TypeHashHelper.Transaction struct that's passed to the _buildTransactionStructHash function. The actual value of executorNonce[execRequest.account][execRequest.executor] is incremented and stored in the _transactionStructHash, but it doesn't affect the value of executorNonce outside of this function.

So, executorNonce[execRequest.account][execRequest.executor] will not be updated in the external state by this code. It will be increased only within the context of creating the Transaction struct and computing the _transactionStructHash. The original mapping value will remain unchanged in the external state.

*AND IT WILL BE SAME FOR EVER*
## Proof of Concept
```solidity 

function _validateExecutionRequest(ExecutionRequest calldata execRequest) internal {
        // Fetch executor registry
        ExecutorRegistry executorRegistry =
            ExecutorRegistry(AddressProviderService._getRegistry(_EXECUTOR_REGISTRY_HASH));

        // Check if executor is valid for given account
        if (!executorRegistry.isExecutor(execRequest.account, execRequest.executor)) {
            revert InvalidExecutor();
        }

        // Empty Signature check for EOA executor
        if (execRequest.executor.code.length == 0 && execRequest.executorSignature.length == 0) {
            // Executor is an EOA and no executor signature is provided
            revert InvalidSignature();
        }

        // Build transaction struct hash
        bytes32 _transactionStructHash = TypeHashHelper._buildTransactionStructHash(
            TypeHashHelper.Transaction({
                to: execRequest.exec.target,
                value: execRequest.exec.value,
                data: execRequest.exec.data,
                operation: uint8(SafeHelper._parseOperationEnum(execRequest.exec.callType)),
                account: execRequest.account,
                executor: execRequest.executor,
                nonce: executorNonce[execRequest.account][execRequest.executor]++
            })
        );

        // Build EIP712 digest for transaction struct hash
        bytes32 _transactionDigest = _hashTypedData(_transactionStructHash);

        // Validate executor signature
        if (
            !SignatureCheckerLib.isValidSignatureNow(
                execRequest.executor, _transactionDigest, execRequest.executorSignature
            )
        ) {
            revert InvalidExecutor();
        }

        // Validate policy signature
        TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH))
            .validatePreExecutorTransaction(
            msg.sender, execRequest.account, _transactionStructHash, execRequest.validatorSignature
        );
    }

```

## Tools Used
v
Deploying a Console to the Same Address Across Different Supported Chains Could Become Impossible
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L56-L72
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L219-L245
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L253-L255


# Vulnerability details

## Impact

In `Brahma`, Users can interact with `SafeDeployer::deployConsoleAccount` to deploy console accounts/wallets. To deploy the wallet to the same address across all supported chains, the user needs to interact with the `deployConsoleAccount` function on all chains providing a list of owners in the same order and using the same unique salt value.



The function `_genNonce` plays an important role in deploying these deterministic safes, as given an owner's hash and salt, this function returns the `saltNonce` which is used in generating the salt used for deploying the safe.

```
    function _genNonce(bytes32 _ownersHash, bytes32 _salt) private returns (uint256) {
        return uint256(keccak256(abi.encodePacked(_ownersHash, ownerSafeCount[_ownersHash]++, _salt, VERSION)));
    }
```
I have observed two possible issues with the current method of generating nonces in the `_genNonce` function. These issues can obstruct the user's ability to deploy wallets to deterministic addresses on different chains, even while using the same set of owners and salt value.

**Weakness 1: Reliance on `ownerSafeCount` for Nonce Generation**

The problem is that the value of `ownerSafeCount` is incremented each time the `_genNonce` function is called within the `_createSafe` function, this can create a disparity between the owner safe count used in generating a nonce in chain A from the value used in chain B.
How `_createSafe` function works is, assuming the first call with nonce1 to `GnosisSafe::createProxyWithNonce` function fails, `genNonce` function is called to generate a new nonce, and then `createProxyWithNonce` function call is retried again. On retry(i.e. `genNonce` call), the `ownerSafeCount` value is incremented.
Now, consider a scenario where the owner's safe count is initially zero (0). If the user's first and second attempts at creating a wallet fail but the third attempt succeeds, the nonce is generated based on an owner safe count of two (2). Now, assuming the same user tries to deploy a wallet with the same identical parameters on a different chain. On this new chain, the wallet creation occurs when `ownerSafeCount` equals zero (0). Consequently, a different nonce is employed in this deployment. As a result, the wallet is deployed to a distinct address. 


**Weakness 2: Reliance on VERSION for Nonce Generation**

The second weakness arises from the reliance on the version of the `SafeDeployer` contract (`VERSION`) for nonce generation. This reliance complicates the ability to deploy a safe to a dete
The same console addresses on other chains can be captured by compromised or malicious owner 
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L47-L71
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L253-L255


# Vulnerability details

## Impact
The same order of `_owners` addresses lets generate the same console address on all chains. But any owner from the list can deploy console accounts on other chains with `_threshold` parameter equals `1` and then change owners in these accounts, i.e. capture these addresses. Another problem is that any user can deploy console accounts on other chains with `_threshold == _owners.length`. So to reconfigure these accounts it is necessary to have signatures from all owners from the list, but this is not always possible (compromising, dismissals, etc.)

## Proof of Concept
It is enough to use the same `_salt` and the same `_owners` (in the same order) to to generate same console address on all chains with the [`deployConsoleAccount`](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/SafeDeployer.sol#L82) function of the `SafeDeployer` contract:
```solidity
    /**
     * @notice Deploys a new console account with or without policy commit and registers it
     * @dev _owners list should contain addresses in the same order to generate same console address on all chains
     * @param _owners list of safe owners
     * @param _threshold safe threshold
     * @param _policyCommit commitment
     * @param _salt salt to be used during creation of safe
     * @return _safe deployed console account address
     */
    function deployConsoleAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
        external
        nonReentrant
        returns (address _safe)
    {
        bool _policyHashValid = _policyCommit != bytes32(0);

        _safe = _createSafe(_owners, _setupConsoleAccount(_owners, _threshold, _policyHashValid), _salt);

        if (_policyHashValid) {
            PolicyRegistry(AddressProviderService._getRegistry(_POLICY_REGISTRY_HASH)).updatePolicy(
                _safe, _policyCommit
            );
        }
        emit ConsoleAccountDeployed(_safe);
    }
```
The `_salt` and `_owners` parameters are used for `nonce` calculation:
```solidity
    function _genNonce(bytes32 _ownersHash, bytes32 _salt) private returns (uint256) {
        return uint256(keccak256(abi.encodePacked(_ownersHash, ownerSafeCount[_ownersHash]++, _salt, VERSION)));
    }
```
Then the `nonce` is used for a new Gnosis Safe creation
```solidity
230            try IGnosisProxyFactory(gnosisProxyFactory).createProxyWithNonce(gnosisSafeSingleton, _initializer, nonce)
231            returns (address _deployedSafe) {
232                _safe = _deployedSafe;
```
Also `_owners` parameter with `_threshold` parameter are used for the console account setup in the [`
Register Wallet unprotected
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/WalletRegistry.sol#L32-L41


# Vulnerability details

## Impact
In the code comments, it states that:
``     * @dev Can only be called by safe deployer or the wallet itself ``
This requires a check for either the safe deployer or the wallet.
However there is no check to verify this case, therefore any address can verify their self as a wallet.
## Proof of Concept
```solidity
     /**
     * @notice Registers a wallet
     * @dev Can only be called by safe deployer or the wallet itself
     */
    //@audit check not followed
    function registerWallet() external {
        if (isWallet[msg.sender]) revert AlreadyRegistered();
        if (subAccountToWallet[msg.sender] != address(0)) revert IsSubAccount();
        isWallet[msg.sender] = true;
        emit RegisterWallet(msg.sender);
    }
```
## Tools Used
Manual Review
## Recommended Mitigation Steps
Use a check for this function, although this function has been implemented wrongly since the safe deployer can never call this function


## Assessed type

Access Control
number of txs of excutors must be excutores + 1 but this loop will +1 in every cycle
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/ConsoleOpBuilder.sol#L127-L128


# Vulnerability details

## Impact
number of txns of excutors must be excutores + 1 but this loop will +1 in every cycle

the code structure is designed the way the number of txns of excutors in `enableExecutorPluginOnSubAccount` function 
counts the length of executors txns and it should be excutors + 1 but it does increase in every cycle of loop

```solidity
 while (i < _numberOfExecutors) {
            txns[i + 1] = Types.Executable({
                callType: Types.CallType.CALL,
                target: executorRegistry,
                value: 0,
                data: abi.encodeCall(ExecutorRegistry.registerExecutor, (subAccount, executors[i]))
            });

            unchecked {
                ++i;
            }
        }
```


## Proof of Concept
 function enableExecutorPluginOnSubAccount(address subAccount, address[] memory executors)
        external
        view
        returns (bytes memory)
    {
        uint256 _numberOfExecutors = executors.length;
        uint256 _numberOfTransactions = _numberOfExecutors + 1;

        Types.Executable[] memory txns = new Types.Executable[](_numberOfTransactions);

        bytes memory enableModuleViaModuleExec = abi.encodeCall(
            IGnosisSafe.execTransactionFromModule,
            (
                subAccount,
                0,
                abi.encodeCall(
                    IGnosisSafe.enableModule, (AddressProviderService._getAuthorizedAddress(_EXECUTOR_PLUGIN_HASH))
                    ),
                SafeHelper._parseOperationEnum(Types.CallType.CALL)
            )
        );

        txns[0] = Types.Executable({
            callType: Types.CallType.CALL,
            target: subAccount,
            value: 0,
            data: enableModuleViaModuleExec
        });

        address executorRegistry = AddressProviderService._getRegistry(_EXECUTOR_REGISTRY_HASH);
        uint256 i;
        while (i < _numberOfExecutors) {
            txns[i + 1] = Types.Executable({
                callType: Types.CallType.CALL,
                target: executorRegistry,
                value: 0,
                data: abi.encodeCall(ExecutorRegistry.registerExecutor, (subAccount, executors[i]))
            });

            unchecked {
                ++i;
            }
        }

        return abi.encodeCall(IGnosisMultiSend.multiSend, (SafeHelper._packMultisendTxns(txns)));
    }
## Tools Used
vscode
## Recommended Mitigation Steps
- consider not adding + 1 in every cycle of while loop instead do it at the end


## Assessed type

Loop
ConsoleFallbackHandler.sol#simulate transaction cannot simulate transaction properly
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/ConsoleFallbackHandler.sol#L104


# Vulnerability details

## Impact

ConsoleFallbackHandler.sol does not use static call or delegate call

## Proof of Concept

In the function 

```solidity
/**
     * @dev Performs a delegetecall on a targetContract in the context of self.
     * Internally reverts execution to avoid side effects (making it static). Catches revert and returns encoded result as bytes.
     * @param targetContract Address of the contract containing the code to execute.
     * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
     */
    function simulate(address targetContract, bytes calldata calldataPayload)
        external
        returns (bytes memory response)
    {
```

the simulate function is meant to simulate a call, so to simulate a call, the transaction should use staticcall to make sure that there is no state change

but the staticcall is not used, instead, call is used

also, the parameter targetContract and calldataPayload is not really used

the simulate call is not marked as payable, so the code cannot simulate call behavior with ETH attached

In short, the simulate function cannot really simulate cal

## Tools Used

Manual Review

## Recommended Mitigation Steps

mark the simulate function payable and use static call to simulate the transction properly


## Assessed type

Access Control
No function to remove a subaccount
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/registries/WalletRegistry.sol#L43-L55


# Vulnerability details

## Impact
A subaccount that has been taken over by an attacker can not be removed.

## Proof of Concept
The WalletRegistry.sol contract has a `registerSubAccount()` function but does not have another function that can remove a subAccount in case a subAccount is compromised.

```solidity
 /**
     * @notice Registers a sub account for a Safe
     * @param _wallet Console account address, owner of sub account
     * @param _subAccount Sub account address to register
     * @dev Can only be called by safe deployer
     */

    function registerSubAccount(address _wallet, address _subAccount) external {
        if (msg.sender != AddressProviderService._getAuthorizedAddress(_SAFE_DEPLOYER_HASH)) revert InvalidSender();
        if (subAccountToWallet[_subAccount] != address(0)) revert AlreadyRegistered();//@audit this condition does not mean already registered. How does he even change a wallet subaccount.
        subAccountToWallet[_subAccount] = _wallet;
        walletToSubAccountList[_wallet].push(_subAccount);
        emit RegisterSubAccount(_wallet, _subAccount);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement a removeSubAccount() function to remove a subAccount that has been compromised.


## Assessed type

Other
Console account cannot execute a transaction on a sub account unless it registers itself as an executor
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/ExecutorPlugin.sol#L69
https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/ExecutorPlugin.sol#L112


# Vulnerability details

The Executor is an account authorized to make module transactions on a subAccount via ExecutorPlugin. The executor is assigned/registered by the subaccount created by the console account. 
But the console account itself cannot execute the transaction & is dependent on the executor to carry it out because if the console account tries to execute, the transaction will revert.

## Proof of Concept

`executeTransaction()` nables executors to raise execution requests that will be executed via a module transaction. It calls `_validateExecutionRequest(execRequest);` to validate the request.

```solidity
File: ExecutorPlugin.sol

    function executeTransaction(ExecutionRequest calldata execRequest) external nonReentrant returns (bytes memory) {
        _validateExecutionRequest(execRequest);         /// @note - issue arises here

        bytes memory txnResult = _executeTxnAsModule(execRequest.account, execRequest.exec);

        TransactionValidator(AddressProviderService._getAuthorizedAddress(_TRANSACTION_VALIDATOR_HASH))
            .validatePostExecutorTransaction(msg.sender, execRequest.account);

        return txnResult;
    }
```
In `_validateExecutionRequest()`, the `if` condition checks if the executor is valid for a given account & reverts otherwise.
```solidity
File: ExecutorPlugin.sol

     function _validateExecutionRequest(ExecutionRequest calldata execRequest) internal {
        // Fetch executor registry
        ExecutorRegistry executorRegistry =
            ExecutorRegistry(AddressProviderService._getRegistry(_EXECUTOR_REGISTRY_HASH));

        // Check if executor is valid for given account
        if (!executorRegistry.isExecutor(execRequest.account, execRequest.executor)) {   /// @note - returns false for console account
            revert InvalidExecutor();
        }
        ..........
```
The issue lies that the creator of the subaccount that is the console account itself cannot execute the transaction on its subaccount if the subaccount does not registers the console account as an executor.
The `isExecutor()` will returns `false` in such a situation.

```solidity
File: ExecutorRegistry.sol

    function isExecutor(address _subAccount, address _executor) external view returns (bool) {
        return subAccountToExecutors[_subAccount].contains(_executor);
    }
```

## Impact
The transaction will always revert if a console account tries to execute a transaction on its subaccount.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Automatically add the owner of the subaccount (console account) as an executor.


## Assessed type

Error
The WalletRegistry.sol#registerWallet() function can be used to register wallet by anyone.
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/registries/WalletRegistry.sol#L35-L40


# Vulnerability details

## Impact
Anyone can register wallet allowing anyone to set the `iswallet[msg.sender]` to true for themselves allowing them to exploit other functions.

## Proof of Concept
From the comment on the `registerWallet()` function below, the `registerWallet()` function `Can only be called by safe deployer or the wallet itself`. However, anyone can call the `registerWallet()` function to set the `isWallet` state as there is no access control.

```solidity
 /**
     * @notice Registers a wallet
     * @dev Can only be called by safe deployer or the wallet itself
     */
    function registerWallet() external {//@audit anyone can call this function and set isWallet state.
        if (isWallet[msg.sender]) revert AlreadyRegistered();
        if (subAccountToWallet[msg.sender] != address(0)) revert IsSubAccount();
        isWallet[msg.sender] = true;
        emit RegisterWallet(msg.sender);
    }
``` 

## Tools Used
Manual review of comments and implementation of the function.

## Recommended Mitigation Steps
Add access control to the `registerWallet()` function to ensure that only safe deployer or the wallet itself can call the the function as mentioned in the function comment.




## Assessed type

Access Control
No proper validation of Singleton
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/TransactionValidator.sol#L197


# Vulnerability details

## Bug Description
`Operators`, `executors`, or the `Main Console account` can execute transactions on behalf of a `SubAccount`. `SubAccounts` must have an enabled `SafeModerator` guard, which checks whether the guard and handler have not been disabled or updated, and whether the owner console, acting as a module, has not been disabled.
But there is no check to verify whether the user has changed the current singleton to another, malicious one.

## Proof-of-Concept
Anyone from the list mentioned above can send a transaction to change the Singleton (`GnosisSafe` implementation). `For operators and executors, the TRUSTED VALIDATOR should allow this.`

Any of these actors can set the address of the `GnosisSafe` implementation to a malicious one. For example, the internal function `TransactionValidator._checkSubAccountSecurityConfig`, which calls the `IGnosisSafe.isModuleEnabled` function, may return an invalid value. As a result, the owner console as a module can be disabled, but `isModuleEnabled` will still return 'true:
```solidity
function _checkSubAccountSecurityConfig(address _subAccount) internal view {
        ///code

        address ownerConsole =
            WalletRegistry(AddressProviderService._getRegistry(_WALLET_REGISTRY_HASH)).subAccountToWallet(_subAccount);

        // Ensure owner console as a module has not been disabled
        if (!IGnosisSafe(_subAccount).isModuleEnabled(ownerConsole)) revert InvalidModule();
    }
``` 

## Impact
Since there is no check in place to prevent `subAccounts` from altering the `Singleton`, it is possible to set a malicious one and manipulate function calls on the `subAccount`. I understand that this can be done manually and bypass all security measures, but the protocol itself does not verify this, as it does with `guard, fallback handler, and module`.

## Tools Used
Manual

## Recommended Mitigation Steps
Consider validating whether the address of the `Gnosis Safe` implementation has not changed to a malicious one using the `SafeModerator` guard within the `_checkSubAccountSecurityConfig` function.


## Assessed type

Invalid Validation