xanoutas/solidity-security-findings huggingface dataset:
Token Loss on Failed Destination Chain Call
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L77


# Vulnerability details

## Impact
The burnAndCallAxelar function allows users to burn tokens from their account with the intention of executing a cross-chain call. However, the tokens are burned before the cross-chain call is attempted. If the cross-chain call fails on the destination chain for any reason, such as a contract error or network issue, the burned tokens may become unrecoverable. This poses a risk of financial loss for users and a potential lack of trust in the contract's cross-chain functionality.

## Proof of Concept

    function burnAndCallAxelar(
    uint256 amount,
    string calldata destinationChain
    ) external payable whenNotPaused {
    // check destinationChain is correct
    string memory destContract = destChainToContractAddr[destinationChain];

    if (bytes(destContract).length == 0) {
      revert DestinationNotSupported();
    }

    if (msg.value == 0) {
      revert GasFeeTooLow();
    }

    // burn amount
    TOKEN.burnFrom(msg.sender, amount);

    bytes memory payload = abi.encode(VERSION, msg.sender, amount, nonce++);

    _payGasAndCallContract(destinationChain, destContract, payload);
    }

Exploitation Scenario:

User Initiation: A user initiates a cross-chain transaction using the burnAndCallAxelar function. They specify an amount of tokens to burn, along with the destination chain and contract.

Token Burning: The function first burns the specified amount of tokens from the user's account. This is a crucial step, and the tokens are now removed from the user's balance.

Cross-Chain Call Attempt: After burning the tokens, the function attempts to execute a cross-chain call to the specified destination chain and contract with a payload.

Cross-Chain Call Failure: If, for any reason, the cross-chain call fails on the destination chain (e.g., due to contract errors, network issues, or other unforeseen circumstances), the transaction fails on the destination chain.

Unrecoverable Token Loss: Since the tokens were already burned in the user's account, and the cross-chain call failed, there is no mechanism in place to refund or return the tokens to the user. As a result, the user experiences an unrecoverable token loss.
## Tools Used
Manual review

## Recommended Mitigation Steps
Implement error handling mechanisms that allow for token redemption in case of a cross-chain call failure. This may involve creating a mechanism to refund or return tokens to the user if the destination chain transaction fails.


## Assessed type

Invalid Validation
Critical Reentrancy Vulnerability in _mintIfThresholdMet Function
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L350
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L112


# Vulnerability details

## Impact
The _mintIfThresholdMet function contains a severe reentrancy vulnerability that can be exploited by malicious contracts.
 When the TOKEN.mint function is called and tokens are minted for txn.sender, the txn.amount is subsequently set to 0 by deleting txnHashToTransaction[txnHash]. 
This creates an opportunity for a reentrant contract to repeatedly call _mintIfThresholdMet, effectively draining the contract's balance, and leading to a potential financial loss. The impact of this vulnerability is critical, as it compromises the security and integrity of the contract's token minting mechanism.


## Proof of Concept

_mintIfThresholdMet take txnHash that contains the sender and the amount, mint the tokens,then txnHashToTransaction[txnHash] to set them to 0.

  function _mintIfThresholdMet(bytes32 txnHash) internal {
    bool thresholdMet = _checkThresholdMet(txnHash);
    Transaction memory txn = txnHashToTransaction[txnHash];
    if (thresholdMet) { //check
      _checkAndUpdateInstantMintLimit(txn.amount);
      if (!ALLOWLIST.isAllowed(txn.sender)) {
        ALLOWLIST.setAccountStatus(
          txn.sender,
          ALLOWLIST.getValidTermIndexes()[0],
          true
        );
      }
      TOKEN.mint(txn.sender, txn.amount); //interact
      delete txnHashToTransaction[txnHash]; // effect
      emit BridgeCompleted(txn.sender, txn.amount);
    }
  }


Certainly, here are the details of the vulnerability in the _mintIfThresholdMet function:

Vulnerability Description:

The _mintIfThresholdMet function contains a reentrancy vulnerability that allows a malicious contract to repeatedly call the function, resulting in the draining of the contract's balance and potential financial loss.

Exploitation Scenario:

Initial Invocation: A legitimate transaction triggers the _mintIfThresholdMet function, which checks if a certain threshold is met. If the threshold is met, tokens are minted for txn.sender, and the txn.amount is set to 0.

Malicious Reentrant Contract: A malicious contract (a reentrant contract) is deployed, which calls the _mintIfThresholdMet function.

Reentrant Call: The reentrant contract repeatedly calls _mintIfThresholdMet, effectively entering a loop. Each time it does so, it triggers the minting of tokens for the txn.sender, and the txn.amount is set to 0.

Financial Drain: As the reentrant contract continues to call the function, it drains the contract's balance by minting tokens for the same txn.sender and setting txn.amount to 0 each time.

Potential Loss: The reentrant contract can cause significant financial losses by depleting the contract's token balance.


## Tools Used
Manual review

## Recommended Mitigation Steps
 Change the order of operations so that txn.amount is set to 0 befor
Cross-chain smart contract call can revert and burned tokens in source chain can't be recovered 
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/SourceBridge.sol#L61-L82
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L85-L114


# Vulnerability details

## Summary
AXELAR_GATEWAY.callContract doesn't revert on failure in destination chain , _execute function can revert for varies reasons such as not passing require statements, or low gas so tokens remained burned in source chain and can't be recovered . 
## Impact
Loss of bridged tokens 
## Tools Used
Manual Review 
## Recommended Mitigation Steps
Consider sending the tokens to a recovery address in case the transaction fails.





## Assessed type

Other
Price Manipulation Through Vulnerability in simulateRange Function 
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L90
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/rwaOracles/RWADynamicOracle.sol#L104


# Vulnerability details

## Impact
The simulateRange function, although designed for simulation and testing purposes, could potentially be exploited in a sandwich attack scenario.
 A malicious actor could front-run a user's transaction by using a flash loan to manipulate the price, subsequently calling simulateRange to obtain the new high price. This price manipulation could lead to financial losses for the user and gains for the attacker in a back-run transaction. The impact of this vulnerability is significant, as it undermines the fairness and integrity of the contract's price mechanisms.


## Proof of Concept


     * @notice External helper function used to simulate the derivation of the prices returned
     *         from the oracle, given a range and a timestamp
    function simulateRange(
    uint256 blockTimeStamp,
    uint256 dailyIR,
    uint256 endTime,
    uint256 startTime,
    uint256 rangeStartPrice
    ) external view returns (uint256 price) {
    uint256 length = ranges.length;
    Range[] memory rangeList = new Range[](length + 1);
    for (uint256 i = 0; i < length; ++i) {
      rangeList[i] = ranges[i];
    }
    if (startTime == ranges[0].start) {
      uint256 trueStart = (rangeStartPrice * ONE) / dailyIR;
      rangeList[length] = Range(startTime, endTime, dailyIR, trueStart);
    } else {
      Range memory lastRange = ranges[ranges.length - 1];
      uint256 prevClosePrice = derivePrice(lastRange, lastRange.end - 1);
      rangeList[length] = Range(
        lastRange.end,
        endTime,
        dailyIR,
        prevClosePrice
      );
    }
    for (uint256 i = 0; i < length + 1; ++i) {
      Range memory range = rangeList[(length) - i];
      if (range.start <= blockTimeStamp) {
        if (range.end <= blockTimeStamp) {
          return derivePrice(range, range.end - 1);
        } else {
          return derivePrice(range, blockTimeStamp);
        }
      }
    }
    }

Exploitation Scenario:

Front-running Flash Loan Attack: An attacker monitors the contract for pending user transactions, particularly those involving buying assets. They initiate a flash loan to temporarily borrow a significant amount of the asset.

Price Manipulation: The attacker conducts a large buy transaction using their flash loaned assets. This causes a temporary increase in the asset's price within the contract.

Calling simulateRange for New Price: With the price artificially inflated, the attacker calls the simulateRange function, providing parameters that reflect the new high price. The function returns this manipulated high price.

User Transaction Loss: A user, unaware of the price manipulation, submits a transaction to buy the same asset. They end up paying a higher price due to the manipulated value obtained from simulate
Updating closed ranges with overrideRange() causes incorrect prevRangeClosePrice. 
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L186-L236
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L295-L300


# Vulnerability details

## Impact
- Incorrect starting prices for ranges
- Incorrect historical price derivations
- Loss of accurate price data history


## Proof of Concept
updating closed ranges with overrideRange() can cause an incorrect prevRangeClosePrice value. Here is a more detailed explanation:

The prevRangeClosePrice is intended to be the derived price from the previous range at the end of that range. This value is then used as the starting price for the next range.

The issue occurs if you call overrideRange() on a closed range. For example:


Range 1: 
   Start: 1000 
   End: 2000
   prevRangeClosePrice: 10

Range 2:
   Start: 2000  
   End: 3000
   prevRangeClosePrice: 20

If you then call overrideRange() to modify Range 1 after it is already closed:

overrideRange(0, 1500, 2500, 5%, 30)

This will update Range 1 but leave Range 2's prevRangeClosePrice as 20. When in reality, based on the new Range 1 values, the prevRangeClosePrice for Range 2 should be 25.

So Range 2's starting price is now incorrect based on the previous range.

This happens because overrideRange() just overwrites the closed range values without recalculating subsequent ranges.

## Tools Used
Manual
## Recommended Mitigation Steps 
Recalculate all subsequent range's prevRangeClosePrices if a closed range is overridden


## Assessed type

Other
Emergency Withdrawal Function Allows Admin to Potentially Steal User Funds
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L324


# Vulnerability details

## Impact
The impact of this finding is that the rescueTokens function in the smart contract allows the owner to potentially steal all the  tokens held within the contract when it is paused or unpaused.
 This presents a significant security risk to users and investors, as it enables the admin to execute a "rug pull" by transferring all funds to their own address, leaving users with no recourse for recovery.

## Proof of Concept


    function rescueTokens(address _token) external onlyOwner {
    uint256 balance = IRWALike(_token).balanceOf(address(this));
    IRWALike(_token).transfer(owner(), balance);
    }

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider implementing decentralized mechanisms for contract pausing and emergency actions that require consensus among multiple parties or a time-lock mechanism to give users a chance to react and get back their funds.



## Assessed type

Rug-Pull
Admin can steal all funds from user via arbitrary execution
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L165
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDYFactory.sol#L131


# Vulnerability details

## Impact
The execute function in the smart contract presents a critical security vulnerability, as it allows entities with the guadian and owner roles to execute arbitrary transactions, potentially leading to the theft of user assets .
 This vulnerability will result in severe financial losses for users and a significant breach of trust.

## Proof of Concept

SourceBridge.sol

    function multiexcall(
    ExCallData[] calldata exCallData
    ) external payable override onlyOwner returns (bytes[] memory results) {
    ..
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      ..
    }

and rUSDYFactory.sol 

    function multiexcall(
    ExCallData[] calldata exCallData
    ) external payable override onlyGuardian returns (bytes[] memory results) {
    ..
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
    ..
    }


attack scenario:
Admin pass a contract he control as exCallData[i].target argument,
.call to  controlled exCallData[i].target contract,
exCallData[i].data can abi.encodeWithSelector(
ERC20.transferFrom.selector,
victim,
admin,
token.balanceOf(victim)
)

thus admin will steal the users tokens .


## Tools Used
Manual review

## Recommended Mitigation Steps
I recommend the protocol not let the any entities to perform arbtirary execution and to restrict this function.



## Assessed type

Rug-Pull
Token Transfer Calculation Error in wrap and unwrap Functions
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L437
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L454


# Vulnerability details

## Impact
The identified issue concerns the wrap and unwrap functions within a smart contract, where the incorrect calculation of the transferred token amount can lead to a significant financial impact. 
In the wrap function,user transfer _USDYAmount  , while in the unwrap function, the incorrect calculation of _USDYAmount / BPS_DENOMINATOR leads to users receiving a smaller amount than they initially provided. 
This discrepancy will result in financial losses for users who expect to retrieve the amount they wrapped. The impact of this issue is critical, as it affects the integrity of the contract's financial operations and user trust.


## Proof of Concept

when minting, wrap() mint  _USDYAmount * BPS_DENOMINATOR, and transfer _USDYAmount to the contract

    function wrap(uint256 _USDYAmount) external whenNotPaused {
    ..
    _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);

    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    ..
    }
 
but when burning, unwrap() burns just  _USDYAmount, not  _USDYAmount * BPS_DENOMINATOR),
and transfers to the user usdyAmount / BPS_DENOMINATOR, not his usdyAmount 

    function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    ..
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
    _burnShares(msg.sender, usdyAmount); 

    usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
    ..
    }

## Tools Used
Manual review

## Recommended Mitigation Steps
Modify the unwrap function to ensure that users receive the exact _USDYAmount they provided to the contract. Remove the incorrect division by BPS_DENOMINATOR so it is


    function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    ..
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
     _burnShares(msg.sender, usdyAmount * BPS_DENOMINATOR); 
      usdy.transfer(msg.sender, usdyAmount);
    ..
    }


## Assessed type

Math
The user may not receive tokens on the destination chain
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/SourceBridge.sol#L96-L102


# Vulnerability details

Any application that wants Axelar to automatically execute contract calls on the destination chain needs to pay the AxelarGasService smart contract on the source chain.  Occasionally, the prepaid gas within the `SourceBridge.burnAndCallAxelar` function for the Gas Receiver contract may fall short.
[SourceBridge.sol#L96-L102](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/SourceBridge.sol#L96-L102)

## Proof-of-Concept
The user called the burnAndCallAxelar function and specified msg.value.  However, it turned out to be insufficient for the automatic execution of the `execute` function that will call the [DestinationBridge._execute()](https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L85) function from the Axelar network side.

This situation can arise when the destination chain becomes congested due to a high volume of transfers or other factors.

## Impact
Transaction have commonly become "stuck" in the pipeline because the execution of the transaction failed on the destination chain due to insufficient provided gas. Meanwhile, the user's USDY tokens on the source chain were burned.


## Tools Used
Manual

## Recommended Mitigation Steps
Add more gas to the Gas Receiver contract on the source chain. You need to implement this function in the `SourceBridge` smart contract:
```solidity
function addNativeGas(
    bytes32 txHash,
    uint256 logIndex,
    address refundAddress
) external payable override;
```

This function pays additional gas, in native tokens on its EVM source chain, to a transaction that needs it, referenced by its txHash.
Further information about this function can be found [here](https://docs.axelar.dev/dev/general-message-passing/gas-services/increase-gas).


## Assessed type

Context
Unauthorized Token Transfer Due To Bad Approval Mechanism
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L277
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L310
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L493


# Vulnerability details

## Impact
The identified issue pertains to the token transfer mechanism within a smart contract. Specifically, the contract allows a user to approve themselves for a certain amount of tokens without actually deducting this amount from their balance. Subsequently, when the user (or an approved spender) initiates a transfer, the contract deducts the allowance but fails to check and deduct the actual balance of the sender. This flaw allows any user to effectively mint tokens out of thin air, leading to a severe security vulnerability.


## Proof of Concept


    function approve(address _spender, uint256 _amount) public returns (bool) {
    _approve(msg.sender, _spender, _amount);
    return true;
    }

an attacker calls approve as he sets _spender as his address

    function _approve(
    address _owner,
    address _spender,
    uint256 _amount
    ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
    }


thus  allowances[attacker][attacker] = _amount;
but _amount is not deducted from _owner's balance

    function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
    ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
    }


when he calls transferFrom currentAllowance would be allowances[attacker][attacker] which is _amount,
the _amount s transfered to _recipient, which can be his address as well,
_approve deduct the _amount from the allowance, means allowances[attacker][attacker] = 0,
while it was not deducted from _owner's balance, thus the _amount was sent from the contract's balance.


## Tools Used
Manual review

## Recommended Mitigation Steps
Modify the transferFrom function to not only check the allowance but also ensure that the sender has a sufficient balance. Deduct the transferred amount from the sender's balance.


## Assessed type

Token-Transfer
Unprotected `initialize` functions can front-run by MEV or by an Attacker.
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L109-L118


# Vulnerability details

## Vulnerability Detail

It should be avoided that the implementation for proxy contracts can be initialized by third parties. This can be the case if the initialize function is unprotected. Since the implementation contract is not meant to be used directly without a proxy delegate-calling it is recommended to protect the initialization method of the implementation by initializing on deployment.

Changing the proxies implementation to a version that does not protect the initialization method may allow someone to front-run and initialize the contract if it is not done within the same transaction.

`Issues in our contract`

The `initialize` function of `rUSDY` contracts are unprotected and vulnerable to attack by MEV bot or an Attacker.

```solidity
function initialize(
    address blocklist,
    address allowlist,
    address sanctionsList,
    address _usdy,
    address guardian,
    address _oracle
) public virtual initializer {
    __rUSDY_init(blocklist, allowlist, sanctionsList, _usdy, guardian, _oracle);
}
```

[rUSDY.sol - Lines 109 - 118](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L109-L118)

## Impact

These parameters are very critical to operate the protocol which can be manipulated.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Initialize unprotected implementation contracts in the implementation’s constructor. Protect initialization methods from being called by unauthorized parties or ensure that deployment of the proxy and initialization is performed in the same transaction.


## Assessed type

MEV