xanoutas/solidity-security-findings huggingface dataset:
Console accounts can be deployed without enforced policy commitment.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L56-L71
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L65
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L56-L71


# Vulnerability details

## Impact

The `deployConsoleAccount` function allows deploying an account without a policy commitment. This seems insecure as there is no enforced policy. 

## Proof of Concept

This is where [deployConsoleAccount allows deploying](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L56-L71) an account without a policy commitment is:

```solidity
function deployConsoleAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
    external
    nonReentrant
    returns (address _safe)
{
    bool _policyHashValid = _policyCommit != bytes32(0);

    // ...

    if (_policyHashValid) {
        PolicyRegistry(AddressProviderService._getRegistry(_POLICY_REGISTRY_HASH)).updatePolicy(
            _safe, _policyCommit
        );
    }
    
    // ...
}
```

The main thing is `_policyHashValid` is a boolean that is set based on `_policyCommit` being non-zero. This allows `_policyCommit` to be passed in as 0, which makes `_policyHashValid` false.

Then the code only registers the policy commitment if `_policyHashValid` is true. So if `_policyCommit` is 0, no policy will be registered.

This seems insecure because there should always be a policy enforced on a deployed account. Allowing `_policyCommit` to be 0 (essentially empty) means accounts can be deployed without a policy.


The impact of allowing `deployConsoleAccount` to deploy accounts without a policy commitment:

Enabling this is that `_policyCommit` is passed as a parameter to `deployConsoleAccount` rather than being required or enforced in the function itself. 

By passing `_policyCommit`, the caller can provide any value including 0. This sets `_policyHashValid` to false if `_policyCommit` is 0.

Then later in the function, there is an if statement checking [`_policyHashValid`](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L65):

```solidity
if (_policyHashValid) {
  // register policy 
}
```

So if `_policyHashValid` is false, it skips registering any policy commitment.

This is a problem for a few reasons:

- Policies in Brahma are meant to govern and control actions on the accounts. Not having a policy means there are no restrictions on what the account can do.

- The console accounts especially should have policies, as they control sub-accounts. A console without policy puts their sub-accounts at risk too.

- Having the choice to deploy with/without policy goes against
Critical flaw: Anyone can deploy accounts without access control.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L56-L71
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L82-L103


# Vulnerability details

## Impact

There is no access control on the `deployConsoleAccount` and `deploySubAccount` functions. Anyone can deploy accounts. There should be some admin role(s) that can call these.

## Proof of Concept

The [`deployConsoleAccount`](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L56-L71) and [`deploySubAccount`](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L82-L103) functions are declared as:

```solidity
function deployConsoleAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
        external
        nonReentrant
        returns (address _safe) 
         {
           ...
         }

function deploySubAccount(address[] calldata _owners, uint256 _threshold, bytes32 _policyCommit, bytes32 _salt)
        external
        nonReentrant
        returns (address _subAcc)
        {
           ...
        }
```

The `external` visibility means these can be called by any external account. There is no `onlyOwner` or other access control modifier on them.

This could allow an attacker to deploy console and sub-accounts at will.

As you can see, these are declared with the `external` visibility modifier. This means they can be called by any external account or contract.

There are no access control modifiers like `onlyOwner` on these functions. That means literally any EOA or contract can call these to deploy new console and sub-accounts within SafeDeployer.

For example:

```solidity

// Attacker EOA can call:
SafeDeployer.deployConsoleAccount(attackerOwners, ...);

// Attacker contract can call: 
SafeDeployer.deploySubAccount(moreAttackerOwners, ...);

```
This is a major security issue because an attacker could fully deploy arbitrary new accounts within the SafeDeployer system. They could then use these accounts to extract funds, modify critical settings, etc.

## Tools Used

Manual

## Recommended Mitigation Steps

An `onlyOwner` modifier could be added:

```solidity 

modifier onlyOwner() {
  require(msg.sender == owner, "Not owner");
  _;
}

function deployConsoleAccount(/*...*/) external onlyOwner nonReentrant {
  // ...
}
```

And the owner address set on deployment:

```solidity
address public immutable owner;

constructor() {
  owner = msg.sender; 
}
```

This would ensure only the deployer of the SafeDeployer contract can create new accounts.


## Assessed type

Access Control
Inherited contract allows manipulation of trusted registry addresses, potentially causing harm.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L42
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L21
https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/AddressProvider.sol#L41
https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/AddressProvider.sol#L97-L105
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L91-L92
https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/AddressProviderService.sol#L44-L47


# Vulnerability details

## Impact

The SafeDeployer.sol contract inherits from `AddressProviderService` which allows updating registry addresses. This could be abused to point to malicious registries. The constructor should save copies of trusted registry addresses instead of relying on `AddressProviderService` state.

## Proof of Concept

The `AddressProviderService` inheritance and reliance on its state for registry addresses occurs in multiple places, but first in the constructor here: https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L42

```solidity
constructor(address _addressProvider) AddressProviderService(_addressProvider) {}
```

These details would help explain this issue fully.

The `SafeDeployer` contract inherits from `AddressProviderService`, as shown on this line: https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L21

```solidity
contract SafeDeployer is AddressProviderService, ReentrancyGuard {
```

`AddressProviderService` contains a mapping called registries, which maps `bytes32` hashes to registry contract addresses: https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/AddressProvider.sol#L41

```solidity
mapping(bytes32 => address) public registries; 
```

It also contains a function called `setRegistry` that allows updating a registry address:

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

The `SafeDeployer` relies on the AddressProviderService's registries mapping to get registry addresses, such as in this line: https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/SafeDeployer.sol#L91-L92

```solidity 
WalletRegistry(AddressProviderService._getRegistry(_WALLET_REGISTRY_HASH)).updatePolicy(
                _safe, _policyCommit
            );
```

Which calls the `_getRegistry` function of `AddressProviderService`:

https://github.com/code-423n4/2023-1
Lack of binding enables reuse of trustedValidator's signature on different transactions.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L121-L127
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L100-L127


# Vulnerability details

## Impact

The `validationStructHas` relies on the `transactionStructHash` provided as a parameter. If an attacker provided a fake transaction digest, they could get an invalid signature approved by using the real `trustedValidator` signature from another transaction.

## Proof of Concept

This is where the `validationStructHash` relies on the untrusted `transactionStructHash` parameter is: https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L121-L127

```solidity
bytes32 validationStructHash = TypeHashHelper._buildValidationStructHash(
  TypeHashHelper.Validation({
    transactionStructHash: transactionStructHash, 
    policyHash: policyHash,
    expiryEpoch: expiryEpoch
  })
);
```

In the [`isPolicySignatureValid`](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L100-L127) function.

This builds the `validationStructHash` using the `transactionStructHash` passed as a parameter.

Attacker could exploit this by:

1. Obtaining a valid signed `validationStructHash` for one transaction.

2. Calling `isPolicySignatureValid` with a different `transactionStructHash`.

3. The real trusted validator signature would still verify, even though it corresponds to a different transaction digest.

This would allow the attacker to approve an invalid transaction by reusing a signature from another transaction. 

## Vulnerability Detail

The `PolicyValidator` constructs a `validationStructHash` using the `transactionStructHash` passed as a parameter. This `validationStructHash` is then signed by the `trustedValidator` to approve the transaction.

The issue is that the `transactionStructHash` can be supplied entirely by the caller. There is no binding between the transaction digest and the account/specific transaction being validated.

This allows an attacker to reuse a trustedValidator's signature from another approved transaction:

1. Attacker obtains a valid signed `validationStructHash` for transaction A. 

2. Attacker calls `isPolicySignatureValid` for transaction B, passing in their own fake `transactionStructHash`.

3. The real `trustedValidator` signature from transaction A verifies successfully, even though it corresponds to a different digest.

4. Transaction B is now approved with the reused signature, despite being invalid.

In essence, the lack of binding between the signature and transaction digest enables what could be considered a "signature replay" attack.

**The impact of this is:**

- Arbitrary transaction approval - Attackers can approve any transaction by reusing signa
Lack of `trustedValidator` validation allows unauthorized approval of transactions.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L132
https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L100-L142


# Vulnerability details

## Impact

There is no validation that the `trustedValidator` address is actually authorized to sign for the account's policy. An attacker could set an unauthorized address as the `TRUSTED_VALIDATOR` and sign arbitrary transactions.

## Proof of Concept

This is the line that could allow an unauthorized `trustedValidator` address is: [L132](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L132)

```solidity
address trustedValidator = AddressProviderService._getAuthorizedAddress(_TRUSTED_VALIDATOR_HASH);
```

In the [`isPolicySignatureValid` function](https://github.com/code-423n4/2023-10-brahma/blob/c217699448ffd7ec0253472bf0d156e52d45ca71/contracts/src/core/PolicyValidator.sol#L100-L142).

This line fetches the `trustedValidator` address from the AddressProviderService's authorized list using the `_TRUSTED_VALIDATOR_HASH` constant. 

However, there is no validation that this authorized address is actually allowed to sign for the specific account's policy being checked.

The `PolicyValidator` contract relies on a `trustedValidator` address that is authorized to cryptographically sign and validate transactions on behalf of an account's policies. 

The `trustedValidator` address is fetched from the AddressProviderService's authorized list using a constant `_TRUSTED_VALIDATOR_HASH`.

The issue is that there is no verification that this authorized address is actually an approved signer for the specific account policy being validated.

This means an attacker could exploit the validator role to arbitrarily approve transactions on accounts:

1. Attacker authorizes their own address as the `_TRUSTED_VALIDATOR` in AddressProviderService. 

2. Attacker calls `isPolicySignatureValid` for the target account, providing their own signature for the transaction.

3. The PolicyValidator sees the attacker's address in the authorized list and uses it to verify the signature.

4. Even though the attacker should not be an approved signer for that policy, the signature is considered valid.

5. Attacker can now approve any transaction on the account by signing it with their unauthorized validator key. 

This bypasses the account's policy checks since the validator role is assumed to be trusted without validation.

This is an example code showing how an unauthorized `trustedValidator` address could be exploited:

```solidity

// Attacker contract
contract Attacker {

  AddressProviderService aps; 
  PolicyValidator pv;

  // Authorize attacker address as TRUSTED_VALIDATOR
  function exploit() external {
    aps.authorize(TRUSTED_VALIDATOR_HASH, address(this));  
  }

  // Craft
Users can potentially provide any address as the '_addressProvider' in 'AddressProviderService'
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `AddressProviderService` contract's constructor allows any address to be set as the `addressProvider`. While it checks that the provided address is not the zero address, it does not validate the legitimacy or trustworthiness of the provided address. This lack of validation means that users can potentially set a malicious contract as the `addressProvider`.

If a malicious address is provided and set as the `addressProvider`, it can lead to unintended and harmful behaviors. Since the `addressProvider` is used throughout the contract to fetch registries and authorized addresses, a malicious provider can return harmful or fraudulent addresses. This can compromise the entire system that relies on the `AddressProviderService` contract, potentially leading to loss of funds or unauthorized actions.

To ensure the security and reliability of the system, it's crucial to ensure that only legitimate and trusted address providers can be set in the `AddressProviderService` contract.


## Proof of Concept

1. **Setup**: Deploy the `AddressProviderService` contract on a test network.
2. **Malicious AddressProvider**: Create a malicious contract that mimics the `AddressProvider` interface. Instead of returning genuine registry or authorized addresses, this malicious contract returns addresses of other malicious contracts or simply random addresses.
3. **Inject Malicious Provider**: Call the constructor of the `AddressProviderService` contract using the address of the malicious address provider created in step 2.
4. **Trigger Malicious Behavior**: Interact with the `AddressProviderService` contract's functions like `_getRegistry` or `_getAuthorizedAddress`. Observe that these functions now return the malicious addresses set up in the malicious provider instead of the genuine ones.
5. **Result**: The system that relies on the `AddressProviderService` contract is now compromised. Depending on the specific malicious behavior coded into the malicious provider, various harmful actions can be taken, including but not limited to unauthorized transactions, data leakage, or other unexpected behaviors.

Source code:

```solidity
   constructor(address _addressProvider) {
        if (_addressProvider == address(0)) revert InvalidAddressProvider();
        addressProvider = AddressProvider(_addressProvider);
    }



```


This proof of concept demonstrates the vulnerability and the potential risks associated with allowing any address to be set as the `addressProvider` without proper validation.

```solidity
// Mock of the genuine AddressProvider
contract GenuineAddressProvider {
    address public registry;
    address public authorizedAddress;

    constructor(address _registry, address _authorizedAddress) {
        registry = _registry;
        authorizedAddress = _authorizedAddress;
    }

    function getRegis
TransactionValidator does not validate gas token address and gas price parameter when validating the transaction
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#L64


# Vulnerability details

## Impact

TransactionValidator does not validate gas token address and gas price parameter when validating the transaction

## Proof of Concept

the safe transaction struct is listed below:

```solidity
    struct SafeTransactionParams {
        Enum.Operation operation;
        address from;
        address to;
        address payable refundReceiver;
        address gasToken;
        address msgSender;
        uint256 value;
        uint256 safeTxGas;
        uint256 baseGas;
        uint256 gasPrice;
        bytes data;
        bytes signatures;
    }
```

However, when validating the transaction, only the parameter from, to, value and data and operation type and signature is used

```solidity
    function validatePreTransactionOverridable(SafeTransactionParams memory txParams) external view {
        // Check if guard or fallback handler is being removed, if yes, skip policy validation
        if (_isConsoleBeingOverriden(txParams.from, txParams.to, txParams.value, txParams.data, txParams.operation)) {
            return;
        }

        // Validate policy otherwise
        _validatePolicySignature(
            txParams.from, txParams.to, txParams.value, txParams.data, txParams.operation, txParams.signatures
        );
    }
```

the parameter tx gas and base gas and gas price and refundReceiver and gas token is not part of the data used to validate the policy signature

this is concerning

a more QA impact is when executing the transaction, the refundReceiver is set to wrong address so a wrong address receive the gas refund

a more severe impact is when executing the transaction, arbitrary amount of gas price and gas amount can be passed in

for certain transaction, the transaction may perform low level call and pass the gas parameter around and if there are insufficient gas passed in, to not block the transaction flow,

the low level call sliently revert, and such slient revert may not. be expected

for example, when executing transaction such as 

```solidity
(bool result, bytes memory data) = address(to).call{gas: gasleft()}(msg.data)
if (!result) {
 // does not revert
}
```

one example transaction is when finalizing the withdraw in L1 optimism bridge, sufficient gas must be provided to finalize the transaction, other wise the fund can be locked in the bridge because transaction sliently revert without reverting the whole transaction

ensuring sufficient gas amount passed in really important

and if the executor pass the gas price too low, the transaction may be pending forever and not executed

## Tools Used

Manual Review

## Recommended Mitigation Steps

validating gas price, gas amount in transaction validator


## Assessed type

Access Control