When a user supplies the gas needed to run the intended smart contract but ignores its sub-calls, this is known as a Gas Griefing attack. Most frequently, this leads to uncontrolled activity that might dangerously affect the business logic.
You will go into great detail about this vulnerability in this blog article, including what it implies, how it affects the smart contract industry and ways to fix it. You will receive some resources you may utilize to look for this kind of vulnerability.
A Gas Griefing attack takes place when a user provides just enough gas to run the specified smart contract but not enough to run its sub-calls. This flaw can be found in smart contract methods that either fail to check the amount of gas needed to perform a sub-call or fail to check the call’s actual returned value.
As an illustration, imagine that you are developing a smart contract-based videogame that needs the user to send some of their ether to another smart contract in order to construct an object. Afterward, the object would be applied within the agreement to make payments within the game. An attacker could trick the smart contract and avoid this payment if it does not verify that Ethereum was correctly transmitted.
The community of smart contract developers is currently unaware of this issue because the majority of Blockchain technologies that facilitate the creation of smart contracts are still fairly new on the market. As a result, the majority of those solutions are still not reliable enough to be used in the development of intricate and secure applications. Additionally, the community is still not particularly aware of many vulnerabilities that might be presented by developers when those vulnerabilities are being built.
An illustration of the Gas Griefing security vulnerability in technical terms
pragma solidity ^0.5.0;
contract Relayer {
function relay (Target target, bytes memory _data) public returns (bool) {
(bool success,) = address (target).call(abi.encodeWithSignature("execute(bytes)", _data)); // then send money to the user
return success;
}
}
contract Target {
bool public result = false;
function execute(bytes memory _data) public {
uint j = 0;
for(uint i;i<100;i++){
j++;
}
result = true;
}
function setresult(bool v) public{
result = v;
}
}
Assume the “Target” contract is the one with which the “Relayer” contract interacts.
The weakest contract is the relayer contract, and line 7 is where the weakness lies. Look closely at this line to notice the smart contract’s attempt to invoke the Target smart contract’s execute() function. The method continues to run without any issues because neither the amount of gas left in the tank nor the call’s outcome is checked. The application functionality may be severely impacted by this behavior.
This only indicates that perhaps the smart contract “Target” did not correctly complete its execution (insufficient gas). Try raising the gas cap and running the smart contract method relay() once again to confirm this. You can now see that the value of the result variable has changed.
By adding gas checks before making the calls, you may prevent this behavior from causing vulnerability in the smart contract.
What effect does the Gas Griefing attack have?
An application reverting to a significant monetary loss can be the result of a Gas Griefing attack. However, the smart contract’s commercial functionality determines how such vulnerability will really affect users.
How can I tell if I’m vulnerable to Gas Griefing?
You only need to search for the code lines that execute an external interaction without outcome verification to find this vulnerability. There is a chance that this vulnerability will be discovered every moment the smart contract transmits, receives, or simply invokes a smart contract function.
Executing external interaction without examining the outcomes, however, does not always imply that now the vulnerability may be exploited or that the effect will be significant. Therefore, you need first to comprehend the commercial semantics of said smart contract in order to better identify and assess this vulnerability.
How may the Gas Griefing vulnerability be avoided?
One of the most challenging challenges is guarding against the gas griefing vulnerability. The complexity of this vulnerability actually stems from the notion that an EVM update is necessary to resolve the problem. To the best of my knowledge, there is currently no solution to this problem. Users are still not protected from this vulnerability by any means, not even the ones suggested by the company Swcregistry.
Let’s first talk about why the suggested solution on the well-liked Swcregistry website might not always function properly.
pragma solidity ^0.5.0;
contract Relayer {
function relay (Target target, bytes memory _data, uint _gaslimit) public returns (bool) {
(bool success, ) = address (target).call(abi.encodeWithSignature("execute(bytes)", _data, _gasLimit));//Swcregistry Fix // then send money to the user
return success;
}
}
contract Target {
bool public result = false;
function execute(bytes memory _data, uint _gaslimit) public {
require(gasleft() >= _gaslimit, 'not enough gas'); //Swcregistry Fix
uint j = 0;
for(uint i;i<100;i++){
j++;
}
result=true;
}
function setresult(bool v) public{
result = v;
}
}
In this case, for the smart contract to function properly, the user must indicate the amount of gas that must be given to the “Target” smart contract. The transaction will reverse if the amount of gas left is less than what was supplied. This does not imply that, in the event that there is not enough gas to complete the implementation of the “Target” smart contract, the Relayer will likewise revert. Additionally, the user can neglect to define or purposefully fail to do so a _gasLimit large enough to run the “Relayer” smart contract while not really the “Target” smart contract.
As a result, it is necessary to execute a new solution because the danger will persist even with the suggested one. The remedy suggested for this weakness is as follows:
The first step of the approach requires the developer to calculate how much gas will be needed for the call to function properly. Additionally, he must increase that value to a maximum, at the very least, to prevent a future increase in petrol prices. This could be carried out as follows:
pragma solidity ^0.5.0;
contract Relayer {
uint public estimatedGasValue=1000000;
uint public gasNeededBetweenCalls = 5000;
function relay (Target target, bytes memory _data) public returns (bool) {
uint256 gasAvailable= gasleft() - gas Needed BetweenCalls;
require(gasAvailable - gasAvailable / 64 >= estimatedGasValue, "not enough gas provided");
(bool success, ) = address (target).call(abi.encodeWithSignature("execute(bytes)", _data, estimatedGasValue)); //Swcregistry Fix // here you need to check if the object or the value you are trying to change or create was succesfuly made.
// In this example you can check the result value to see if the call was successful.
// then send money to the user
return success;
}
}
contract Target {
bool public result = false;
function execute(bytes memory _data) public {
uint j = 0;
for(uint i;i<100;i++){
j++;
}
result = true;
}
function setresult(bool v) public{
result = v;
}
}
You should comprehend the following significant elements of that source code before I begin to discuss this contract:
estimatedGasValue: This variable denotes the quantity of gas needed for the call() function to properly carry out the Target smart contract method.
gasNeededBetweenCalls: The projected cost of gas between lines 9 and 11 is given by the field gasNeededBetweenCalls.
gasAvailable/64: After invoking the Target function, the amount of gas in the contract will still be represented by gasAvailable/64. The reason for the value 64 is that, by default, the smart contract reserves 1/64 of the gas provided to the smart contract that makes the call so that, when that call has ended, the caller smart contract can complete its execution.
When the smart contract reaches line 11, the call() method, line 9 estimates the quantity of gas that’ll be accessible for the contract.
Line 10 determines whether the amount of gas will continue greater than the estimatedGasValue a gain for call() after subtracting that amount from the gas provided by the user.
Actually, this concept is identical to the one put up by the Swcregistry. The main distinction between both is that in this case, the smart contract developer, not the user, determines exactly how much gas should be sent.
This solution can be improved by including routines to set the gasNeededBetweenCalls and estimatedGasValue values because those values might fluctuate over time.
Final Points
A contract that takes generic input and utilizes it to call other contracts (a “sub-call”) via the low-level address may be vulnerable to this attack. call() method, as is frequently the case with transactional relayer and multi-signature contracts.
The contract offers two choices should the call not succeed:
- The entire transaction is rolled back
- Proceed with the execution.
The strategy used above is not ideal since it could fail if gas prices continue to rise over time. Offering the EVM the authority to discern the necessary gas for every call and cancel the transactions if it is insufficient is the greatest solution to this issue.
Join our beta program now https://www.olympix.ai/
Originally published at https://www.linkedin.com.