Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Denial of Service attack

Summary

We are using push payment pattern via a low-level call to send the prize to the winner at RockPaperScissors::_finishGame

(bool success, ) = _winner.call{value: prize}("")

Vulnerability Details

Using .call{value: prize}("") directly to send Ether to an external address is bad because:

  1. Untrusted receiver contracts may have fallback or receive functions that consume more gas or revert unexpectedly.

  2. If the receiver is a smart contract, it might:

    • Revert the transaction

    • Reenter your contract if proper guards aren't in place

    • Fail to receive the prize without notifying the sender

  3. There's no tracking mechanism of unclaimed prizes — if it fails, the funds may get stuck or lost.

Impact - High/ Likelyhood - High

  • Low-level calls can silently fail, and the contract might not properly handle these failures unless explicitly checked.

  • Denial of Service (DoS): A malicious or faulty contract could block other winners from receiving their prize.

Tools Used

Manual Review

Recommendations

Use method : pull over push

mapping(address => uint256) public pendingWithdrawals;
function setWinner(address winner) internal {
pendingWithdrawals[winner] += prize;
}
function claimPrize() external {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, "Nothing to withdraw");
pendingWithdrawals[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Withdrawal failed");
}
Updates

Appeal created

m3dython Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Denial of Service (DoS) due to Unhandled External Call Revert

Malicious player wins a game using a contract that intentionally reverts when receiving ETH, the entire transaction will fail

Support

FAQs

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