51218 sc high oracle callback timing vulnerability causes jackpot prize loss
Submitted on Aug 1st 2025 at 01:25:02 UTC by @KlosMitSoss for Attackathon | Plume Network
Report ID: #51218
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/spin/Spin.sol
Impacts:
Theft of unclaimed yield
Description
Brief/Intro
When a callback from the Supra Router is executed, the current week is calculated based on the current timestamp rather than when the spin was initiated. This creates a timing vulnerability where users can start a spin during an active campaign, but the callback executes after the campaign has ended. When such delayed spins win the jackpot, users receive zero rewards despite legitimately winning and paying the spin fee.
Vulnerability Details
Users who initiate spins in the final weeks of the campaign risk losing legitimately won jackpots due to oracle callback delays beyond their control. The vulnerability occurs in the handleRandomness() function:
function determineReward(uint256 randomness, uint256 streakForReward) internal view returns (string memory, uint256) {
... ...
uint8 weekNumber = uint8(getCurrentWeek()); // Uses current timestamp, not spin initiation time
if (probability < jackpotThreshold) {
return ("Jackpot", jackpotPrizes[weekNumber]); // Returns 0 for weeks > 11
}
... ...
}These delays cannot be controlled and will lead to unfair situations for users that didn't do anything wrong and started their spin during the campaign.
To mitigate this, consider using the timestamp of the startSpin() call such that the current week only relies on the user and not on the delay between the two calls.
Impact Details
The won jackpot cannot be claimed even though the user started the spin while the campaign was ongoing. This leads to users losing legitimately won jackpots which are currently worth up to 100,000 PLUME tokens.
References
Code references are provided throughout the report.
Proof of Concept
Step
In handleRandomness(), determineReward() is called. Inside this function, the week number is calculated based on the current timestamp, not the timestamp of the startSpin() call (https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/spin/Spin.sol#L285-L286).
Step
This week number is used to retrieve the jackpot prize for the week (https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/spin/Spin.sol#L293). However, since the week number is greater than 11 and the campaign only lasts 12 weeks (weeks 0-11), the jackpot prize for week 12 will be 0. The following comment also confirms this: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/spin/Spin.sol#L451
Was this helpful?