🛢️Abusing Ethereum's 63/64 Gas Rule to Manipulate Contract Behavior

Ethereum’s 63/64 gas forwarding rule, while designed to protect against certain types of attacks, can also be exploited in specific scenarios. Malicious users can deliberately supply carefully crafted amounts of gas to cause failures in contract execution, manipulate outcomes, and even force specific parts of a contract to behave unexpectedly.

In this tutorial, we will explore how attackers can abuse the 63/64 gas rule to:

  • Force cross-chain transfers to fail.

  • Trigger catch blocks in try-catch structures.

  • Exploit vulnerabilities in gas-restricted contract interactions.

We will also discuss mitigation strategies to prevent these types of attacks.


Understanding the Exploit: How Gas Manipulation Works

The 63/64 rule comes into play whenever one contract calls another. The calling contract will only forward 63/64ths of its remaining gas to the callee. This means that if the initial gas provided is already low, the callee may not have enough gas to complete its operations, causing the transaction to fail or return a partial result.

An attacker can exploit this by supplying a precise amount of gas when interacting with a smart contract, knowing that the callee will receive less gas due to the 63/64 rule. This can lead to situations where:

  • Cross-chain transfers or external contract calls fail due to insufficient gas.

  • Catch blocks in try-catch constructs are triggered, allowing the attacker to manipulate the contract’s behavior.

  • State-changing operations are not completed because they run out of gas partway through execution.


Example 1: Forcing Cross-Chain Transfers to Fail

Cross-chain operations often involve a call from one smart contract to another, where gas availability is crucial for success. Attackers can deliberately provide just enough gas for the calling contract, knowing that when the 63/64 rule is applied, the remaining gas will be insufficient to complete the cross-chain transfer.

Here’s how it could happen in a vulnerable contract:

function crossChainTransfer(address _to, uint256 _amount) external {
    try externalContract.transfer(_to, _amount) {
        // Transfer succeeded
        emit TransferSuccess(_to, _amount);
    } catch {
        // Fallback or retry logic
        emit TransferFailed(_to, _amount);
    }
}

An attacker could send just enough gas to trigger the try block but leave insufficient gas for the external contract (externalContract.transfer()) to complete the transfer. The catch block would then be executed, potentially leading to unexpected behavior or repeated failed attempts.

Attack Impact:

  • Transaction Fails: The external contract fails to complete the transfer due to low gas, forcing the fallback logic (e.g., reverting the transfer or triggering compensation mechanisms).

  • Exploiting Fallbacks: If the fallback mechanism compensates users in some way, an attacker could repeatedly trigger it to extract funds or cause other damage.

Example 2: Forcing Execution of the catch Block

In Solidity, try-catch blocks are useful for handling failed external calls. However, the 63/64 gas rule can be exploited to force the execution of the catch block by deliberately providing too little gas for the try block to complete successfully.

Consider this contract:

function processTransaction(address _contract, uint256 _amount) external {
    try _contract.call{value: _amount}("") {
        // Success logic
        emit Success();
    } catch {
        // Fallback logic
        emit Failure();
    }
}

In this scenario, the attacker sends just enough gas to enter the try block, but not enough for the contract to finish its execution. This causes the catch block to run, which might include undesirable fallback behavior that can be exploited by the attacker.

Attack Impact:

  • Fallback Abuse: If the fallback logic compensates users, attackers could drain funds or manipulate the contract into an undesired state.

  • Triggered Failures: Repeatedly forcing the catch block can trigger unintended behavior, such as logging failure events, triggering refunds, or updating state variables incorrectly.


Example 3: Causing Contract Calls to Fail

Multi-contract systems often rely on one contract calling another to perform actions. By using the 63/64 gas rule to provide just enough gas, attackers can prevent the callee from completing its operation, which could cause an overall system failure or inconsistent state.

For instance:

function performAction(address target, bytes calldata data) external {
    (bool success, ) = target.call(data);
    require(success, "Call failed");
}

In this case, an attacker can exploit the 63/64 rule by supplying just enough gas to trigger the call() but not enough for the entire operation to succeed. The result? The call() will fail, reverting the transaction. In more complex systems, this could prevent the completion of critical operations or halt contract functionality altogether.

Attack Impact:

  • Denial of Service (DoS): The attacker can repeatedly cause the call to fail, leading to a denial of service for the contract.

  • System Instability: Preventing the completion of key operations could lead to inconsistent states or blocked functions in the system.


Mitigation: Handling the 63/64 Gas Rule

To prevent attackers from exploiting the 63/64 gas rule, the focus should be on ensuring that the callee (called contract) receives enough gas to execute its required operations, even after the 63/64 rule reduces the forwarded gas.

Here are the key strategies to handle this:

  1. Explicitly Allocate Gas in Contract Calls: When making a call to an external contract, you can explicitly define the amount of gas to forward, ensuring that the remaining gas after applying the 63/64 rule is sufficient for the external contract to complete its execution.

(bool success, ) = target.call{gas: gasAmount}(data);
require(success, "External call failed");
  • Here, you ensure that gasAmount is large enough to accommodate both the external contract's needs and the 63/64 gas rule.

  • Use gasleft() to Ensure Minimum Gas: You can use gasleft() to check whether the remaining gas will be enough to forward after the 63/64 rule is applied. This prevents calls from being made if insufficient gas remains for the external call.

uint256 requiredGas = minimumRequiredGas + (minimumRequiredGas / 63);
require(gasleft() >= requiredGas, "Insufficient gas for external call");

Conclusion

The Ethereum 63/64 gas rule can be a source of unintended vulnerabilities if developers are not careful with gas management in smart contracts. By explicitly managing the amount of gas passed to external contracts, ensuring that sufficient gas remains after the 63/64 rule is applied, and using proper gas validation methods (such as gasleft()), developers can avoid many of the pitfalls associated with this rule.

By employing these strategies, smart contracts can become more resilient to gas-based attacks and better handle complex contract interactions where gas constraints can otherwise lead to failures or exploits.

Last updated