Denial-of-Service cyberattack prevents authorized users from accessing a network or computer by limiting access to it. Hackers now frequently use DoS attacks to obstruct the smooth operation of computer-based systems. Distributed attacks are one of the most successful methods for finishing such an attack. The attacker uses up all of the servers’ storage, bandwidth, and memory by delivering additional data and flooding the network.
Due to the immense potential that blockchains contain, they are primarily targeted by DoS attacks that aim to steal money. The most popular cryptocurrencies, such as Bitcoin, Ethereum, and many others, have also been the targets of DoS attacks.
DoS Attack
Denial of Service attacks might differ in form and structure. Volumetric attacks rely on flooding a network with echo requests to use all of its available bandwidth. Syn flooding attacks are analogous to flooding attacks as they also involve overloading the system with requests; nonetheless, the attacker quickly establishes a connection with the server before closing it. Attacks targeting the target network’s capacity to reassemble itself are called fragmentation attacks. Application layer attacks flood the target with queries to find hidden code problems in a network or application. Last but not least, phlashing DoS attacks spread malware and give out fake update requests to irreversibly harm the target network.
DoS types
Unexpected Revert DoS
Let’s use an instance of an auction contract to analyze this Denial of service ( dos attack) on smart contracts. Every time a top bid is submitted, the auction contract gets updated, and if the prior bidder’s offer was lesser than the new one, the difference is refunded.
contract Auction {
address frontRunner;
uint256 highestBid;
function bid() public payable {
require(msg.value > highestBid, "Need to be higher than highest bid"); // Refund the old leader, if it fails then revert
require(payable(frontRunner).send(highestBid), "Failed to send Ether");
frontRunner = msg.sender;
highestBid = msg.value;
}
}
To gain access to the functions of the auction, the attacker contract’s constructor initializes it with the address of the auction contract. The attacker freezes the quantity by calling the “attack()” method in this scenario, stopping the system from restoring the attacker’s initial bid value even in the situation that a greater bid is received.
import "./Auction.sol";
contract Attacker{
Auction auction;
constructor(Auction _auctionaddr){
auction = Auction(_auctionaddr);
}
function attack (){
auction.bid{value: msg.value}();
}
}
The “attacker” agreement without a “receive()” or backup mechanism to repay Ether, which is the cause of the unanticipated revert. As a consequence, the attacker will always be awarded the contract.
Disable Gas Limit DoS.
The maximum quantity of gas that the block can utilize depends on how much processing is required. DoS attacks can take one of two different forms as a result of exceeding the gas cap.
Unbounded Contract Operation with Gas Limit
Payments collapse if their gas usage goes beyond the predetermined limit because there is an established gas limit.
The issue worsens when the assailant has control over the necessary gas. Let’s use an example to grasp this situation better.
struct Payee {
address addr;
uint256 value;
}
Payee[] payees;
uint256 nextPayeeIndex;
function payOut() {
uint256 i = nextPayeeIndex;
while (i < payees.length && msg.gas > 200000) {
payees[i].addr.send(payees[i].value);
i++;
}
nextPayeeIndex = i;
}
The hacker can add a couple of addresses to this contract in trade for a very tiny refund. Every instance the payout method is called, the smart contract reverses the transaction because the gas fee related to each refund pushes the gas cost past the threshold that prevents the reimbursement transactions from occurring.
Therefore, when creating a looping array, appropriate steps must be taken into account.
Block Stuffing
By submitting computationally costly transactions, the attacker wants to prevent transactions from attaching to the blocks. The same transactions are issued repeatedly until the gas limit is reached.
By paying the high gas charge, the attacker ensures that only the desired transactions-and not the others-are added to the blocks.
Owner Action
Numerous contracts include a particular owner address with the authority to start or stop transactions. Since the holder’s address is essential to the process, losing it will prevent the user from sending any tokens and will cause the contract to stop working.
Effects of a DoS attack
In addition to the stoppage of smart contract operations, a DoS assault on the blockchain might result in the following collateral losses:
Bloated ledger
The blockchain ledger is the permanent record of all transactions. The blockchain nodes store a duplicate of each transaction for twofold checks. During a DoS attack, the ledger may become overrun by spam transactions.
Network traffic
A replica of the transaction is sent to each node on the blockchain. The peer-to-peer network may experience a high volume of transactions as a result of DoS flooding, using up the network bandwidth.
Failures of nodes
Blockchain is supported by nodes, which require software to manage enormous volumes of data. During operation flooding, the nodes could run out of memory, which would halt operations.
How can I create smart contracts that are DoS-safe?
By switching your smart contract’s payment system from a push design to a pull one, DoS attacks can be prevented.
The bid() method uses a push model in the Unexpected Revert DoS section to reimburse the former bidder, meaning that the smart contract tries to deliver the money to the appropriate address. By employing the withdraw() function, this is modified to a pull model, where the smart contract needs the deserving address to submit a request for funds:
contract Auction {
address frontRunner;
uint256 highestBid;
mapping(address => uint) public balances;
function bid() public payable {
require(msg.value > highestBid, "Need to be higher than highest bid"); // update previous bidder info
balances[frontRunner] += highestBid; // update new bidder
frontRunner = msg.sender;
highestBid = msg.value;
}
function withdraw() public nonReentrant {
require(msg.sender != frontRunner, "Current frontRunner cannot withdraw");
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
By preventing unbounded loops.
Unbounded loops are those that have no limitations on the total number of iterations. Of course, up to the transaction’s gas limit, a sender is permitted to run functions with unlimited loops. The number of unique calls that software clients can make to a contract is not a restriction, but the maximum allowed computational complexity significantly limits each contract operation and call. As a result, our attention should be on consumption patterns that switch from limited commodity to abundant supply. A software client should make 1000 little contract function requests to accomplish 1000 steps rather than a contract service that performs a step 1000 times.
Sort algorithms should not be used in any smart contract as their operating costs can increase with increasing complexity. In essence, break down on-chain procedures that may have fluctuating costs and significantly increase gas usage into lots of smaller fixed financial operations.
Conclusion
On a distributed computer, computation has a price. The Ethereum blockchain has two features that prevent network abuse: gas costs for calculations and gas limits. A smart contract programmer must create code with consideration for both gas price and gas cap. In this article, we examined DoS assaults, and their results showed how they operate and talked about possible protection.
Join our beta program now https://www.olympix.ai/
Originally published at https://www.linkedin.com.