Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Reentrancy Vulnerability in Batch Contract Due to Delegatecall Usage

Summary

The use of delegatecall in the batch function creates a potential reentrancy vulnerability, allowing malicious contracts to exploit the state of the Batch contract.

Finding Description

The batch function uses delegatecall to execute arbitrary functions specified in the calls array. Since delegatecall executes the called function in the context of the Batch contract, any state changes made by the called function can affect the Batch contract's state.

A malicious contract could be designed to call a function that modifies its own state, which, if executed within the batch function, could lead to unexpected behavior or state corruption. For example, an attacker could manipulate state variables in the Batch contract or perform actions that they shouldn't be allowed to.

Security Guarantees Broken

This vulnerability breaks the security guarantees of:

  • Reentrancy Protection: By allowing an external contract to execute code within the context of the Batch contract, it undermines the integrity of state management.

  • Controlled Execution Flow: The contract cannot guarantee that state is safe to modify when external calls are executed.

Vulnerability Details

The risk arises from the combination of delegatecall and user-supplied input. If an attacker crafts a malicious payload that modifies the contract's state, they can exploit this reentrancy vulnerability to perform unauthorized actions or extract funds.

Example Malicious Input

A malicious contract could send a payload that triggers a function which calls back into the batch function before the state has been finalized, allowing it to bypass checks or double-spend funds.

Impact

The impact of this vulnerability could be severe, potentially leading to loss of funds, unauthorized access to contract functions, or corruption of the contract’s state. Given the nature of delegatecall, the attacker can manipulate the execution flow and potentially steal funds or disrupt normal contract operations.

Proof of Concept

Here’s a simple proof of concept demonstrating the reentrancy risk:

  1. Create a malicious contract that invokes a function in the Batch contract.

  2. Within that function, call the batch function again before the initial call completes.

contract Malicious {
Batch public batchContract;
constructor(address _batchContract) {
batchContract = Batch(_batchContract);
}
function attack(bytes[] calldata calls) external {
batchContract.batch(calls);
}
// This function can be crafted to manipulate state
function maliciousFunction() external {
// Call back into the Batch contract or modify state
// This is where reentrancy could be exploited
}
}

Recommendations

To mitigate the reentrancy risk, implement a reentrancy guard pattern in the batch function. This can be achieved using a simple boolean flag or using the OpenZeppelin ReentrancyGuard library.

Suggested Code Fix

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
abstract contract Batch is ReentrancyGuard {
// ... existing code ...
function batch(bytes[] calldata calls) external nonReentrant {
uint256 count = calls.length;
require(count > 0, "No calls provided");
require(count <= 100, "Too many calls");
for (uint256 i = 0; i < count; ++i) {
(bool success, bytes memory result) = address(this).delegatecall(calls[i]);
if (!success) {
revert Errors.BatchError(result.length > 0 ? result : "Unknown error");
}
}
emit BatchExecuted(count);
}
}

Using the nonReentrant modifier from OpenZeppelin will help prevent reentrant calls and protect the contract's state.

File Location

src/abstracts/Batch.sol

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.