xanoutas/solidity-security-findings huggingface dataset:
`OUSG` & `rOUSG` tokens can not be instant minted under certain condition
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L230-L324


# Vulnerability details

## Impact

The `OUSGInstantManager.sol` contract oversees the minting and redemption processes for OUSG and rOUSG tokens within the Ondo Finance ecosystem. It utilizes `minimumDepositAmount` and `minimumRedemptionAmount` to set the floor on the amount of USDC that can be converted into OUSG tokens and the minimum amount of OUSG tokens that can be redeemed back to USDC, respectively. These thresholds ensure that transactions are of a significant enough size to warrant processing, preventing spam and ensuring efficiency. Additionally, `InstantMintTimeBasedRateLimiter.sol` imposes an `instantMintLimit` (and analogously, an `instantRedemptionLimit`) to cap the amount of tokens that can be minted or redeemed within a certain timeframe, providing a mechanism to manage liquidity and mitigate abuse.

The problem is that the `minimumDepositAmount` for minting exceeds the `instantMintLimit`, or when the `minimumRedemptionAmount` surpasses the `instantRedemptionLimit`. This configuration mismatch leads to a scenario where users are unable to initiate minting or redemption transactions despite having sufficient funds, as the system's constraints paradoxically prevent transactions that meet one criterion but violate another.

As a result, users with the intent and capacity to transact within the defined parameters find themselves unfairly barred due to conflicting system restrictions.

## Proof of Concept

**Scenario 1 - Minting Blocked by Limit Mismatch**:

- The `minimumDepositAmount` is set to 60,000 USDC, while the `instantMintLimit` is 50,000 USDC.
- Alice attempts to mint OUSG tokens with 60,000 USDC.
- Despite exceeding the `minimumDepositAmount`, her transaction is reverted because it surpasses the `instantMintLimit`.

**Scenario 2 - Redemption Blocked by Limit Mismatch**:

- Similarly, if the `minimumRedemptionAmount` for OUSG tokens is set higher than the available `instantRedemptionLimit`, users attempting to redeem their tokens face transaction rejections.

The problem cause when `OUSGInstantManager.sol#minimumDepositAmount` is greater than `InstantMintTimeBasedRateLimiter.sol#instantMintLimit` or `OUSGInstantManager.sol#minimumRedemptionAmount` is greater than `InstantMintTimeBasedRateLimiter.sol#instantRedemptionLimit`. **Important to note here is that this situation and the issue overall is different from the intended instant mint/redeem flow, where users can mint/redeem specific amount over specific duration.**

## Tools Used

Manual Review

## Recommended Mitigation Steps

During the changing of `instantMintLimit` and `instantRedemptionLimit` doesn't allow to can be set with values smaller than `minimumDepositAmount` and `minimumRedemptionAmount`.

- [`_mint()`](https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L278-L324) function:

```solidity
`rOUSG.sol` contract (token) isn't fully compilable with EIP20 standard
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/rOUSG.sol#L572-L606


# Vulnerability details

## Impact

- Non-Overridable `_beforeTokenTransfer()` Function in `rOUSG.sol` Limits EIP-20 Compliance and Extensibility

The `rOUSG.sol` contract's implementation of the `_beforeTokenTransfer()` function without the `virtual` keyword significantly impacts the contract's compliance with EIP-20 standards and its flexibility. EIP-20 (or ERC-20) is a widely adopted standard for Ethereum tokens, ensuring consistent behavior across tokens and compatibility with the broader Ethereum ecosystem. The inability to override `_beforeTokenTransfer()` restricts developers from introducing custom logic for token transfers, such as additional validations, transfer restrictions, or events that could enhance the token's security, usability, or integration capabilities. This limitation could deter adoption, complicate upgrades or extensions, and potentially expose the token to unforeseen risks by limiting the scope of governance or security measures that can be implemented in derived contracts.

## Proof of Concept

The problem is that the [`rOUSG.sol#_beforeTokenTransfer()`](https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/rOUSG.sol#L586-L606) function is implemented without the `virtual` keyword, as described [here](https://blog.pessimistic.io/auditors-notes-erc20-integration-tips-3b150021b8c0#:~:text=Most%20tokens%20rely%20on%20an%20implementation%20from%20Open%20Zeppelin%2C%20so%20that%3A%20If%20_beforeTokenTransfer%20or%20_afterTokenTransfer%20are%20overridden%2C%20these%20functions%20must%20also%20be%20virtual%20and%20call%20super%3B) in an `auditor's notes` which outlines best practices for ERC-20 token integrations, including the need for `_beforeTokenTransfer` and `_afterTokenTransfer` to be `virtual` and call `super` when overridden in derived contracts.

```solidity
/**
 * @dev Hook that is called before any transfer of tokens. This includes
 * minting and burning.
 *
 * Calling conditions:
 *
 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
 * will be transferred to `to`.
 * - when `from` is zero, `amount` tokens will be minted for `to`.
 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
 * - `from` and `to` are never both zero.
 *
 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
 */
function _beforeTokenTransfer(address from, address to, uint256) internal view {
  // Check constraints when `transferFrom` is called to facliitate
  // a transfer between two parties that are not `from` or `to`.
  if (from != msg.sender && to != msg.sender) {
    require(_getKYCStatus(msg.sender), "rOUSG: 'sender' address not KYC'd");
  }

  if (from != address(0)) {
    // If not minting
    require(_getKYCStatus(from), "rOUSG: 'from' address not KYC'd");
  }

  if (to != address(0)) {
    // If not burning
    req
No check for transfer result in "retrieveTokens".
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L819


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
There is no check for transfer results for transfer function.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
  function retrieveTokens(
    address token,
    address to,
    uint256 amount
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    IERC20(token).transfer(to, amount);
  }
}
## Tools Used

## Recommended Mitigation Steps
use safetransfer  instead of transfer.


## Assessed type

ERC20
unbounded loop in   "multiexcall" can cause out of gas.
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/rOUSGFactory.sol#L125


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
As we are doing an unbounded loop in multiexcal function.This can cause a out of gas.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.


There is no length limit in the function multiexcall.

 function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
  @>  for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

## Tools Used

## Recommended Mitigation Steps

There should be a length limit for  ExCallData.


## Assessed type

DoS
The flash loan attack can bricked the protocol.
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L278
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L388


# Vulnerability details

**Description:** 
The `ousgInstantManager` contract is providing functionality to mint OUSG or rOUSG tokens in exchange of USDC. But there is a mint limit for certain time block until the lastResetInstantMintTime + resetInstantMintDuration. The attacker can perform the USDC flash loan attack by mint the limit for time block and than in next can redeem the whole USDC which the attacker deposited to reach the mint limit and by redeeming the attacker can also reach the redeem limit for certain time block. And it will make the mint and redeem functionality for other genuine users unable for certain time period. The attacker just need to pay the mint and redeem fee if any set by the protocol to execute this attack. 

**Proof of Concept:**
Let's see how this attack will take place:
1. To make this attack more effective the attacker will mint the tokens at the start of the new time block when the currentInstantMintAmount would be 0.
2. The attacker can call the `mint` or `mintRebasingOUSG` function to reach the mint limit for certain time block.
3. `_mint` function: The `_mint` function calls the `_checkAndUpdateInstantMintLimit` to check that requested mint amount is under the mint limit for that certain time period.
```javascript
function _mint(
    uint256 usdcAmountIn,
    address to
  ) internal returns (uint256 ousgAmountOut) {
    require(
      IERC20Metadata(address(usdc)).decimals() == 6,
      "OUSGInstantManager::_mint: USDC decimals must be 6"
    );
    require(
      usdcAmountIn >= minimumDepositAmount, 
      "OUSGInstantManager::_mint: Deposit amount too small"
    );
@>  _checkAndUpdateInstantMintLimit(usdcAmountIn); 
    if (address(investorBasedRateLimiter) != address(0)) {
      investorBasedRateLimiter.checkAndUpdateMintLimit( 
        msg.sender,
        usdcAmountIn //passing usdc amount
      );
    }

    require(
      usdc.allowance(msg.sender, address(this)) >= usdcAmountIn,
      "OUSGInstantManager::_mint: Allowance must be given to OUSGInstantManager"
    );

    uint256 usdcfees = _getInstantMintFees(usdcAmountIn); 
    uint256 usdcAmountAfterFee = usdcAmountIn - usdcfees; 

   
    uint256 ousgPrice = getOUSGPrice();
    ousgAmountOut = _getMintAmount(usdcAmountAfterFee, ousgPrice); 
    require(
      ousgAmountOut > 0,
      "OUSGInstantManager::_mint: net mint amount can't be zero"
    );

    // Transfer USDC
    if (usdcfees > 0) {
      usdc.transferFrom(msg.sender, feeReceiver, usdcfees); 
    }
    usdc.transferFrom(msg.sender, usdcReceiver, usdcAmountAfterFee); 

    emit MintFeesDeducted(msg.sender, feeReceiver, usdcfees, usdcAmountIn);

    ousg.mint(to, ousgAmountOut); 
  }
```
4. `_checkA
Blocklisted KYCed account will not be able to redeem OUSG/rOUSG for USDC
Audit: 2024-03-ondo-finance-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L455


# Vulnerability details

## Impact
Blocklisted KYCed user will not be able to redeem OUSG or rOUSG for USDC
**impact-** High,  **likelihood-** low

## Proof of Concept
currently USDC is sent directly to the msg.sender when they redeem their OUSG/rOUSG, However if the users address is blocklisted in the usdc contract the usdc transfer
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L455
 will revert and the user will be unable to redeem their tokens henceforth

## Tools Used
Manual review

## Recommended Mitigation Steps
An overload redeem function where a receiver address can be specified and a receiver can be added to the internal _redeem function like so:
```sol
function _redeem(
    uint256 ousgAmountIn,
    address receiver
  ) internal returns (uint256 usdcAmountOut) {

//...code...

  usdc.transfer(receiver, usdcAmountOut);
}
```


## Assessed type

Access Control
Missing `payable` modifier in ExecutorPlugin.executeTransaction(): Restricts Use of Native Assets (ETH) with Transactions
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A registered `executor` for a submodule cannot send ETH (native assets) with a transaction because the `payable` modifier is missing in the `executeTransaction()` function.

It's essential to address this issue to ensure full compatibility and functionality for transactions involving native assets.

## Proof of Concept
If a user(executor) wants to make a transaction to send ETH to the submodule and swap it for USDC, it won't be possible because of the absence of the `payable` modifier in the executeTransaction function.

[ExecutorPlugin.sol executeTransaction()](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/ExecutorPlugin.sol#L68)

```solidity
    function executeTransaction(
        ExecutionRequest calldata execRequest
    ) external nonReentrant returns (bytes memory) {
        ...
    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps
Add `payable` modifier in the [executeTransaction()](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/ExecutorPlugin.sol#L68) function





## Assessed type

Payable
Nonce is not incremented after using signature for policy validation
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Nonce is not incremented after using signature for policy validation

## Proof of Concept

In PolicyValidator.sol

there is a function

```solidity
    function isPolicySignatureValid(
        address account,
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        bytes calldata signatures
    ) external view returns (bool) {
        // Get nonce from safe
        uint256 nonce = IGnosisSafe(account).nonce();

        // Build transaction struct hash
        bytes32 transactionStructHash = TypeHashHelper._buildTransactionStructHash(
            TypeHashHelper.Transaction({
                to: to,
                value: value,
                data: data,
                operation: uint8(operation),
                account: account,
                executor: address(0),
                nonce: nonce
            })
        );

        // Validate signature
        return isPolicySignatureValid(account, transactionStructHash, signatures);
    }
```

the code query the nonce from the safe wallet

but after consume the nonce to validate the policy signature, the nonce is not incremented

this allows signature replay and the same signature can be used multiple to bypass the isPolicyValidation check until the expire expires or until the nonce is incremented on the safe wallet side

## Tools Used

manual review

## Recommended Mitigation Steps

increment the nonce to not code reuse the nonce and signature when validating the policy signature validation


## Assessed type

Access Control
Everyone can disable policy of any brahama console account 
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Everyone can disable policy of any brahama console account 

if you look at the function `disablePolicyOnConsole` it designed to disable the policy and set guards to 0 which is important decision of any account but the problem is everyone can disable random people account cause there is No validation of caller at all at any kind. 

badactor can disable every accounts policy when they want and set the gnosis guards to 0 address 

```solidity 
  /**
     * @notice Provides multicall bytecode for disabling a policy on brahma console account
     * @dev Disables policy, by setting guard to address(0) on Safe, setting fallback handler to address(0) on Safe
     * @param account address of brahma console account
     * @return callData for multicall execution
     */

```

## Proof of Concept
https://github.com/code-423n4/2023-10-brahma/blob/a6424230052fc47c4215200c19a8eef9b07dfccc/contracts/src/core/ConsoleOpBuilder.sol#L68
```solidity
 /**
     * @notice Provides multicall bytecode for disabling a policy on brahma console account
     * @dev Disables policy, by setting guard to address(0) on Safe, setting fallback handler to address(0) on Safe
     * @param account address of brahma console account
     * @return callData for multicall execution
     */
    function disablePolicyOnConsole(address account) external pure returns (bytes memory) {
        Types.Executable[] memory txns = new Types.Executable[](2);

        txns[0] = Types.Executable({
            callType: Types.CallType.CALL,
            target: account,
            value: 0,
            data: abi.encodeCall(IGnosisSafe.setGuard, (address(0)))
        });

        txns[1] = Types.Executable({
            callType: Types.CallType.CALL,
            target: account,
            value: 0,
            data: abi.encodeCall(IGnosisSafe.setFallbackHandler, (address(0)))
        });

        return abi.encodeCall(IGnosisMultiSend.multiSend, (SafeHelper._packMultisendTxns(txns)));
    }


```
## Tools Used
vscode
## Recommended Mitigation Steps
- consider adding logic of validating callers for function which limits the caller to be owner of account. 


## Assessed type

Access Control
Insufficient validation of contracts when setting authorised address.
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/AddressProvider.sol#L77-L90
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/AddressProvider.sol#L84
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/AddressProvider.sol#L131


# Vulnerability details

## Impact
```governance``` can set Malicious contract as authorised address and since the ```AddressProvider.sol``` is a singular source of truth an attacker can craft an exploit to abuse ```_authorizedAddress``` privileges.

## Proof of Concept
A miniaturised POC is shown below.

in the ```AddressProvider.sol``` when ```setAuthorizedAddress(...)``` is called to set ```_authorizedAddress``` as an authorised address with ```_overrideCheck``` set to ```false```, the checks performed before updating the state is not sufficient to ensure that a malicious contract is not added as an authorised address.

```solidity
    function setAuthorizedAddress(bytes32 _key, address _authorizedAddress, bool _overrideCheck) external {
        ...

        /// @dev skips checks for supported `addressProvider()` if `_overrideCheck` is true
>        if (!_overrideCheck) {
            /// @dev skips checks for supported `addressProvider()` if `_authorizedAddress` is an EOA
>           if (_authorizedAddress.code.length != 0) _ensureAddressProvider(_authorizedAddress);
        }

        authorizedAddresses[_key] = _authorizedAddress;

        ...
    }
```

the [```_ensureAddressProvider(...)```](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/AddressProvider.sol#L131) deploys the ```IAddressProviderService``` at the ```_authorizedAddress``` thereby handing over control to the ```_authorizedAddress``` as shown below.

```solidity
   function _ensureAddressProvider(address _newAddress) internal view {
       if (IAddressProviderService(_newAddress).addressProviderTarget() != address(this)) {
           revert AddressProviderUnsupported();
       }
   }
```

#### POC
An attacker can exploit this vulnerability either directly or by proxy
1. Proxy Scenario is
- An attacker uses his Malicious proxy contract with a ```fallback``` and deploys a v1 implementation contract at address ```X``` that implements an ```addressProviderTarget()``` function that points to or returns ```address(addressProvider)```.
- After successfully authorising the malicious contract, attacker upgrades implementation contract to v2 at address ```Y``` that implements a ```addressProviderTarget()``` function that points to or returns a different address.

2. Direct Scenario is shown in the coded POC below
- Attacker implements ```addressProviderTarget()``` function in his malicious contract that points to or returns ```address(addressProvider)``` but has some malicious code in the function and other
registerWallet in WalletRegistry missing access control
Audit: 2023-10-brahma-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

registerWallet in WalletRegistry missing access control

## Proof of Concept

the wallet can be [registered](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/WalletRegistry.sol#L35) by calling registerWallet

```solidity

    /**
     * @notice Registers a wallet
     * @dev Can only be called by safe deployer or the wallet itself
     */
    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);
    }
```

as the comment suggest, the method should only be able to called by safe deployer or wallet itself

however, the check that if msg.sender is safe deployer is missing

anyone can register as wallet then call sensitive function such as [updatePolicy](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/PolicyRegistry.sol#L52) from PolicyRegistry.sol

## Tools Used

Manual Review

## Recommended Mitigation Steps

add validation 

```solidity
 if (msg.sender != AddressProviderService._getAuthorizedAddress(_SAFE_DEPLOYER_HASH)) revert InvalidSender();
```


## Assessed type

Access Control