Denial of Service Attacks
In April 2022, a popular NFT project called Akutar conducted a successful Dutch auction to raise funds, amassing 11,539.5 ETH. However, when processing refunds for previous community pass holders, a flaw in their smart contract prevented operations, locking all funds within the contract due to a DoS vulnerability.
You can find more detailed information in EXPLAINED: THE AKUTARS NFT INCIDENT (APRIL 2022)
Denial of Service (DoS) attacks in the Web2 context typically involve overwhelming a server with excessive traffic, rendering it unable to serve legitimate requests. In the Web3 realm, such attacks exploit vulnerabilities to disrupt smart contract operations.
Vulnerability Example
Let’s explore a simplified contract, GameOfFunds
, that demonstrates this type of vulnerability. The contract allows players to deposit ETH at the beginning and intends to refund these deposits once the game concludes.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract GameOfFunds {
bool public refundsComplete;
mapping(address => uint256) public deposits;
address[] public participantList;
// Players deposit ETH into the contract
function contribute() external payable {
require(!refundsComplete, "Game Over");
require(msg.value > 0, "Please deposit ETH");
deposits[msg.sender] = msg.value;
participantList.push(msg.sender);
}
// Refund players at the end of the game
function processRefunds() external {
require(!refundsComplete, "Game Over");
for (uint256 i = 0; i < participantList.length; i++) {
address participant = participantList[i];
uint256 refundAmount = deposits[participant];
(bool success, ) = participant.call{value: refundAmount}("");
require(success, "Refund Failed!");
deposits[participant] = 0;
}
refundsComplete = true;
}
function getBalance() external view returns(uint256) {
return address(this).balance;
}
}
This contract is vulnerable because the processRefunds()
function uses the call
method, which activates the fallback function of recipient addresses. A malicious contract can disrupt this process.
contract Malicious {
fallback() external payable {
revert("DoS Attack!");
}
function initiateAttack(address gameAddress) external payable {
GameOfFunds game = GameOfFunds(gameAddress);
game.contribute{value: msg.value}();
}
}
Prevention Strategies
- Ensure external contract calls, including fallback functions, do not interfere with or halt critical operations, allowing processes like refunds to continue even if individual transactions fail.
- Maintain contract functionality even if key participants are permanently absent, and allow users to withdraw refunds themselves rather than distributing them in batches.
- Prevent contracts from unintentionally self-destructing and avoid infinite loops to ensure stable and secure contract operations.