The SpookySwap
contract contains a vulnerability allowing an attacker to mint an NFT onto the contract’s address by sending a minimal amount of ETH (e.g., 1 wei). If the attacker does not complete the transaction by calling the resolveTrick
function, the NFT remains "stuck" on the contract address indefinitely. This prevents the contract owner from either reclaiming the NFT or obtaining its full purchase value.
The vulnerability is located in the trickOrTreat
function, specifically in the code block handling the "trick" scenario (i.e., where the treat costs double). When a user sends insufficient ETH for a doubled-cost treat, the function mints an NFT to the contract's address and records it as pending. However, the contract lacks a mechanism to enforce the completion of this purchase. If the resolveTrick
function is not called by the original buyer, the NFT remains locked on the contract address.
77-92:
In the case of an attacker attempting to exploit this vulnerability with a minimal amount of ETH (for example, 1 wei):
If random = 1
or falls within the standard pricing scenario (random != 2
), the check require(msg.value >= requiredCost, "Insufficient ETH sent for treat");
will revert the transaction due to insufficient ETH, causing the attacker to pay only the gas fee for each reverted attempt.
Therefore, the transaction will only succeed when random == 2
, allowing the attacker to pay only the gas fee on repeated attempts until they reach the desired scenario.
This approach makes the attack possible but reduces its cost, as the attacker will only be paying gas fees for each reverted transaction until he achieves an random == 2
outcome.
This vulnerability allows a malicious user to mint an NFT for a minimal ETH amount and effectively lock it on the contract, thus:
Preventing the contract owner from receiving the NFT’s full value.
Potentially accumulating "stuck" NFTs on the contract’s balance, which may reduce contract functionality and cause financial losses for the owner.
VS
Implement an Expiration Mechanism: Introduce a timestamp when each pending NFT is minted to the contract address. After a defined period (e.g., 24 hours), the contract owner should be able to reclaim or resell unresolved NFTs.
Allow Owner Reclaim: Add a function allowing the contract owner to transfer unresolved NFTs back to their address or to an escrow address after the expiration period has passed. This function should only be accessible for NFTs pending beyond the set timeframe.
Enforce Minimum Payment: Ensure trickOrTreat
requires a minimum ETH amount (such as the NFT’s standard price or half its price in the "treat" scenario) to mitigate the risk of malicious users locking NFTs for negligible amounts.
The protocol can work correctly with more than 20000 tokens in it. It is informational.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.