Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

[M-01] Vulnerability Report: Denial of Service (DoS) Due to Block Gas Limit in enterRaffle Function

Summary

The enterRaffle function of the given smart contract is vulnerable to a Denial of Service (DoS) attack due to the potential to exceed the block gas limit. This vulnerability arises from the nested loop utilized for checking duplicate addresses, which can result in excessive gas consumption and consequently, transaction failure.

Vulnerability Details

The enterRaffle function allows a user to enter a raffle by providing an array of player addresses. The function requires the sent value to match the product of the entrance fee and the number of new players. It then iterates through the newPlayers array, adding each player to the players array. Subsequently, it employs a nested loop to check for duplicate player addresses among all players. The nested loop has a time complexity of O(n^2), making the gas cost of this operation grow quadratically with the number of players.

Impact

An attacker can exploit this vulnerability by sending a large array of unique addresses to the enterRaffle function, causing the transaction to fail due to exceeding the block gas limit. This attack can be repeated, blocking legitimate users from entering the raffle and thus denying service.

POC

Add this POC to the foundry test

function test_DoS_gas_block_limit() public {
// Create a large list of unique addresses (e.g., 1000 addresses)
address[] memory newPlayers = new address[](1000);
for (uint256 i = 0; i < newPlayers.length; i++) {
// Assuming each new address is unique
newPlayers[i] = address(i + 1);
}
// Attempt to enter the raffle with a large list of unique addresses
(bool r, ) = address(puppyRaffle).call{
value: entranceFee * newPlayers.length
}(
abi.encodeWithSelector(
puppyRaffle.enterRaffle.selector,
newPlayers
)
);
// The transaction should fail due to exceeding the block gas limit
assert(!r);
}

Tools Used

  • Solidity ^0.8.0

  • Truffle for testing

Recommendations

  1. Limit the number of players that can enter the raffle in a single transaction to a reasonable amount that will not exceed the block gas limit.

  2. Optimize the duplicate check: Instead of using a nested loop, consider using a mapping to check for duplicate addresses, which would reduce the time complexity from O(n^2) to O(n).

  3. Implement gas checks within the function to ensure that sufficient gas remains for the function to complete execution, and fail gracefully if not.

Example:

function enterRaffle(address[] memory newPlayers) public payable {
require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
for (uint256 i = 0; i < newPlayers.length; i++) {
require(gasleft() > 200000, "PuppyRaffle: Insufficient gas");
players.push(newPlayers[i]);
}
for (uint256 i = 0; i < players.length - 1; i++) {
require(gasleft() > 200000, "PuppyRaffle: Insufficient gas");
for (uint256 j = i + 1; j < players.length; j++) {
require(players[i] != players[j], "PuppyRaffle: Duplicate player");
}
}
emit RaffleEnter(newPlayers);
}
Updates

Lead Judging Commences

Hamiltonite Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

denial-of-service-in-enter-raffle

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.