In Solidity, low-level calls such as call(), delegatecall(), and staticcall() return a boolean value indicating whether the call succeeded or failed.
Unlike high-level function calls, these low-level calls do not automatically revert the transaction if they fail. Therefore, it is crucial to check the return value to ensure that the operation succeeded. Failing to do often results in a vulnerability due to silent failures.
In the following example, the contract uses the low-level call() function to transfer Ether to a recipient.
However, the return value is not checked, leading to a bug where the contract assumes the transfer succeeded, even if it failed (e.g., due to insufficient gas, or if the recipient cannot accept the ETH due to absence of fallback function).
Unlike high-level function calls, these low-level calls do not automatically revert the transaction if they fail. Therefore, it is crucial to check the return value to ensure that the operation succeeded. Failing to do often results in a vulnerability due to silent failures.
In the following example, the contract uses the low-level call() function to transfer Ether to a recipient.
However, the return value is not checked, leading to a bug where the contract assumes the transfer succeeded, even if it failed (e.g., due to insufficient gas, or if the recipient cannot accept the ETH due to absence of fallback function).
The above example is just very trivial as there is no real business logic behind it, its just to describe this issue.
So imagine a contract which handles some refunds where the actual ether value is stored somewhere and refunds are allocated stricly. In such an operation where anyone can invoke a permissionless refund function, one can simply invoke this function with insufficient gas that the call silently reverts - however, the storage will still be changed to refunded.