Unverified Parameters in Low-Level Calls
Overview
- Severity: High
- Confidence: Medium
- Affected Versions: All
What are Unverified Parameters in Low-Level Calls?
Unverified parameters in low-level calls occur when a smart contract uses functions like `call`, `delegatecall`, or `staticcall` without properly validating the input parameters. This can lead to unexpected behavior, vulnerabilities, or even exploitation of the contract.
Why are Unverified Parameters in Low-Level Calls Dangerous?
Using unverified parameters in low-level calls can be dangerous because:
- It can lead to execution of unintended functions or actions.
- It may allow attackers to manipulate the contract's state in unexpected ways.
- It can result in loss of funds or unauthorized access to contract functionality.
- It undermines the security and integrity of the contract.
Technical Example of Vulnerable Code
pragma solidity ^0.8.0;
contract VulnerableContract {
address public targetContract;
constructor(address _targetContract) {
targetContract = _targetContract;
}
function unsafeCall(bytes memory data) public {
(bool success, ) = targetContract.call(data);
require(success, "Call failed");
}
}
In this example, the `unsafeCall` function allows arbitrary data to be passed to the `targetContract` without any verification. This could potentially lead to the execution of unintended functions or manipulation of the contract's state.
Technical Example of Fixed Code
pragma solidity ^0.8.0;
contract SecureContract {
address public targetContract;
constructor(address _targetContract) {
targetContract = _targetContract;
}
function safeCall(bytes4 functionSelector, bytes memory params) public {
require(functionSelector != bytes4(0), "Invalid function selector");
require(params.length > 0, "Params must not be empty");
bytes memory data = abi.encodePacked(functionSelector, params);
(bool success, ) = targetContract.call(data);
require(success, "Call failed");
}
}
In this improved version, we've introduced parameter verification. The function selector and parameters are passed separately, allowing for more specific checks. We ensure that the function selector is not empty and that parameters are provided. This helps prevent the execution of unintended functions and provides a more controlled interface for low-level calls.