Skip to content

Latest commit

 

History

History
149 lines (103 loc) · 8.89 KB

040.md

File metadata and controls

149 lines (103 loc) · 8.89 KB

Glamorous Plum Baboon

High

Potential Reentrancy Vulnerability in executeBorrow Due to State Update Delay

Summary

ValidationLogic.validateBorrow being called before updating the state will cause a reentrancy vulnerability for the protocol as malicious actors will exploit the lack of state updates to reenter the function and manipulate the borrow process.

Root Cause

The choice to call ValidationLogic.validateBorrow before updating the state in BorrowLogic.executeBorrow is a mistake as it allows a malicious actor to exploit the lack of updated state to perform a reentrancy attack, manipulating the borrow process.

https://github.com/sherlock-audit/2025-01-aave-v3-3/blob/main/aave-v3-origin/src/contracts/protocol/libraries/logic/BorrowLogic.sol#L60-L141

Internal Pre-conditions

  1. The actor needs to call ValidationLogic.validateBorrow to set the borrow validation check to pass without the state being updated in executeBorrow.
  2. The state variables related to the borrower's debt or collateral position must remain unchanged between the validateBorrow call and subsequent operations.
  3. The actor needs to perform an external call within the same transaction to manipulate the borrowing process while the state remains inconsistent.

External Pre-conditions

  1. The actor needs to manipulate an external dependency, such as IVariableDebtToken or IAToken, to behave unexpectedly during the external call in executeBorrow.
  2. The external protocol or token involved in the borrow process must fail to revert or properly handle unexpected actions triggered by the malicious call.
  3. The actor needs to ensure that external interactions (e.g., oracle updates or token transfers) remain unverified or unchecked during the execution of the borrow process.

Attack Path

  1. Malicious actor calls executeBorrow in the BorrowLogic library, supplying crafted parameters to trigger the borrow process.
  2. ValidationLogic.validateBorrow is called, and the malicious actor ensures the validation passes without issues.
  3. Before the state is updated within the executeBorrow function, the malicious actor executes a reentrant call by exploiting external contract interactions (e.g., calls to IVariableDebtToken or IAToken).
  4. During the reentrant call, the malicious actor manipulates the borrow process or drains funds before the contract state is finalized.
  5. The original executeBorrow call resumes, updating the state based on manipulated or unexpected external changes, completing the exploit.

Impact

The decision to call ValidationLogic.validateBorrow before updating the state in the executeBorrow function exposes the contract to a reentrancy vulnerability. A malicious actor could exploit this flaw, leading to the following potential impacts:

  1. Manipulation of Borrow Process:

    • By triggering a reentrant call during the borrow execution, an attacker can bypass the intended state updates. This can allow them to manipulate the borrow process, potentially borrowing more funds than they are entitled to or executing malicious actions undetected.
  2. Increased Financial Risk:

    • A successful exploit could result in users being able to withdraw more than their allowable collateral or borrow funds they should not be able to access. This could destabilize the liquidity pool, affect reserve balances, and lead to financial losses for the protocol and its users.
  3. Loss of Trust:

    • The exposure to reentrancy attacks undermines the security and reliability of the protocol. Users expect that the borrow function operates securely, and any exploit that allows malicious behavior would result in a loss of trust in the platform.
  4. Protocol Exploitation:

    • If attackers are able to manipulate borrow limits and balances, they could trigger cascading effects on other parts of the protocol, including debt ceilings, collateral usage, and liquidation processes. This could lead to systemic issues and large-scale exploitation across the platform.

This vulnerability represents a critical security risk and should be addressed immediately to safeguard the integrity of the borrowing mechanism and protect both the protocol and its users. Implementing the checks-effects-interactions pattern and reentrancy guards is essential to mitigate this risk and ensure the continued security of the contract.

PoC

Proof of Code for Reentrancy Vulnerability in BorrowLogic.executeBorrow

  1. Scenario Setup:

    • Assume the attacker has some funds already supplied as collateral.
    • The attacker has the ability to trigger a reentrant call to executeBorrow during the external validation call (ValidationLogic.validateBorrow).
  2. Reentrancy Attack Steps:

    • The attacker calls executeBorrow to borrow funds.
    • During the execution, ValidationLogic.validateBorrow is invoked, and it does not yet affect the state of the contract (such as the borrower's debt or collateral).
    • The attacker injects malicious code via the validateBorrow function (assuming it's an external call or the attacker controls part of the validation logic). This allows them to trigger the same executeBorrow function before the state has been updated.
    • The attacker exploits the delay in state updates, manipulating the borrow process and gaining access to more funds than they should be entitled to.

Attacker Contract Example

The following is an illustrative contract that can trigger the reentrancy attack by invoking executeBorrow within a malicious ValidationLogic.validateBorrow call:

// Malicious contract to exploit reentrancy vulnerability
pragma solidity ^0.8.10;

import {BorrowLogic} from './BorrowLogic.sol';
import {ValidationLogic} from './ValidationLogic.sol';
import {DataTypes} from '../types/DataTypes.sol';

contract MaliciousAttacker {
    address public victimAddress;
    address public borrowLogicContract;
    address public user;

    constructor(address _victimAddress, address _borrowLogicContract, address _user) {
        victimAddress = _victimAddress;
        borrowLogicContract = _borrowLogicContract;
        user = _user;
    }

    // Malicious function to initiate the reentrancy attack
    function attack() external {
        // Perform the first borrow
        uint256 amountToBorrow = 1000;
        DataTypes.ExecuteBorrowParams memory params = DataTypes.ExecuteBorrowParams({
            asset: victimAddress,
            user: user,
            onBehalfOf: user,
            amount: amountToBorrow,
            interestRateMode: DataTypes.InterestRateMode.VARIABLE,
            releaseUnderlying: true,
            reservesCount: 1,
            oracle: address(0),
            userEModeCategory: 0,
            priceOracleSentinel: address(0),
            referralCode: 0
        });

        // Calling the victim contract to initiate the borrow, which will call ValidationLogic.validateBorrow
        BorrowLogic.executeBorrow(
            victimAddress, 
            victimAddress, 
            victimAddress, 
            params
        );
    }
}

How the Attack Works:

  1. Attacker Contract Setup:

    • The attacker deploys a contract that holds the logic for performing the attack. The contract is designed to call executeBorrow on the target contract (victim contract).
  2. Triggering Reentrancy:

    • The attacker initiates a borrow by calling executeBorrow, which internally calls ValidationLogic.validateBorrow.
    • While validateBorrow is executing, the attacker can trigger another call to executeBorrow (before the state is updated), effectively borrowing additional funds.

In short

This proof of concept demonstrates how the vulnerability in BorrowLogic.executeBorrow could allow an attacker to exploit the lack of state updates before the external validation call, triggering a reentrancy attack. The state changes should have been made before any external calls to avoid this risk.

Mitigation

Implement the Checks-Effects-Interactions Pattern The checks-effects-interactions pattern is a fundamental best practice for preventing reentrancy attacks. The core idea is to always update the state of the contract before interacting with external contracts or making external calls. This prevents malicious actors from manipulating the contract state during an external call.

Checks: Verify the conditions of the transaction (e.g., validating borrow requests). Effects: Update the contract's internal state (e.g., debt, collateral, or balances). Interactions: Interact with external contracts or make transfers after the internal state has been updated

2, Use Reentrancy Guards The ReentrancyGuard provided by OpenZeppelin helps to prevent reentrancy attacks by ensuring that a function can only be called once per transaction. Adding a nonReentrant modifier to sensitive functions can help ensure that reentrant calls cannot be made