Low-Level Calls Returning True for Non-Existent Contracts
Vulnerability Overview: Low-Level Calls
Low-level functions such as call
, delegatecall
, and staticcall
in Solidity are commonly used to interact with external contracts. These calls do not revert on failure by default, and instead return a boolean value indicating whether the call was successful. However, this can be misleading since these calls return true
even if the target address does not exist or contains no code.
When a contract performs a low-level call to an address that is not a valid contract, it may still proceed as if the operation was successful. This can result in major logic failures, including allowing funds to be mismanaged, unauthorized transfers, or bypassing security mechanisms.
How the Exploit Happens
Here’s a simplified scenario of how this exploit can be triggered:
Low-Level Call to Non-Existent Contract: A contract uses a low-level call to interact with another contract or address.
No Validation of Target Address: The calling contract does not verify if the target address is a valid contract.
False Success: Even if the target address is non-existent, the low-level call returns
true
, leading the calling contract to believe the operation succeeded.Potential Exploitation: An attacker could exploit this by providing non-existent or malicious addresses as targets for low-level calls, causing unintended behavior, including fund loss or bypassing important checks.
Mitigation: Validating Contract Existence Before Low-Level Calls
To avoid this vulnerability, it is essential to verify that the target address is a valid contract before making a low-level call. One of the simplest ways to do this is by using Solidity’s extcodesize
or the OpenZeppelin Address.isContract()
function, which checks if an address is a contract by ensuring that it contains bytecode.
Conclusion
Low-level calls in Solidity are powerful but come with inherent risks, particularly when interacting with non-existent addresses. By default, these calls return true
even if the target address is invalid, leading to potential vulnerabilities and unintended behavior in smart contracts.
To mitigate this issue:
Always check if the target address is a valid contract using
extcodesize
or OpenZeppelin’sAddress.isContract()
function.Revert on failure: Ensure that low-level calls are properly handled, and revert the transaction if the call fails.
Write comprehensive tests to ensure your smart contracts handle external interactions securely and as expected.
By following these best practices, you can significantly reduce the risk of exploits caused by low-level calls returning true
for non-existent contracts, ensuring that your smart contracts are more secure and robust.
Key Takeaways:
Low-level calls (
call
,delegatecall
,staticcall
) returntrue
by default, even for non-existent contracts.Always validate that the target address is a contract before making a low-level call.
Use OpenZeppelin’s
Address.isContract()
to safely verify the existence of a contract.Handle errors properly and revert on failure to prevent false success indicators.
Last updated