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

Predictable Timestamp-Based Randomness Enables Guaranteed Maximum Allocations

Root + Impact

Description

  • The PizzaDrop protocol is designed to distribute random APT allocations to registered users through the get_random_slice() function. Each user should receive a genuinely random amount between 100-500 units, ensuring fair and unpredictable distribution across all participants. The randomness mechanism is intended to prevent users from gaming the system to obtain preferential allocations.

  • The get_random_slice() function uses timestamp::now_microseconds() as its sole source of randomness, making allocations completely predictable. The allocation formula random_amount = 100 + (timestamp % 401) allows sophisticated users to calculate their exact allocation before registration and manipulate timing to guarantee maximum (500-unit) or minimum (100-unit) rewards. This predictable behavior violates the protocol's core fairness assumption and enables systematic exploitation.


The vulnerability stems from the randomness implementation in the get_random_slice() function:

#[randomness]
entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
let time = timestamp::now_microseconds(); @> // Predictable timestamp source
let random_val = time % 401; @> // Deterministic modulo operation
let random_amount = 100 + random_val; @> // Fully calculable result (100-500)
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}

Risk

Likelihood:

  • Technical users regularly analyze smart contract code before interacting with protocols, making discovery of the predictable timestamp formula highly probable during normal due diligence processes.

  • Registration timing is user-controlled through the register_pizza_lover() function, enabling attackers to execute multiple registration attempts with precise timing coordination to achieve desired allocation outcomes.

Impact:

  • Systematic unfair advantage enables sophisticated users to guarantee maximum 500-unit allocations while regular users receive random 100-500 unit amounts, creating a 5x economic disparity that fundamentally undermines the protocol's fair distribution model.

  • Protocol integrity violation destroys the core randomness assumption, potentially leading to user loss of confidence, reduced participation, and reputational damage when the predictable allocation pattern becomes publicly known.

Proof of Concept


The following test demonstrates the predictable nature of the allocation system by successfully predicting exact allocation amounts based on timestamp manipulation:

#[test(deployer = @pizza_drop, user = @0x123, framework = @0x1)]
fun test_h1_predictable_randomness_vulnerability(
deployer: &signer,
user: &signer,
framework: &signer
) acquires State, ModuleData {
// Test setup code omitted for brevity
// Test Case 1: Target specific allocation by setting timestamp
timestamp::update_global_time_for_test_secs(1000000400); // Set predictable timestamp
let current_timestamp = timestamp::now_microseconds();
let predicted_remainder = current_timestamp % 401; // Calculate expected remainder
let predicted_amount = 100 + predicted_remainder; // Predict allocation
// Execute registration with controlled timing
register_pizza_lover(deployer, user_addr);
let actual_amount = get_claimed_amount(user_addr);
// Verification: Prediction matches reality
assert!(actual_amount == predicted_amount, 1);
}

Recommended Mitigation

Replace the predictable timestamp-based randomness with proper cryptographic randomness using Aptos Framework's secure randomness API:

#[randomness]
entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
- let time = timestamp::now_microseconds();
- let random_val = time % 401;
+ let random_bytes = randomness::u64_range(0, 401);
let random_amount = 100 + random_val;
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}
Updates

Appeal created

bube Lead Judge 11 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Predictable randomness

The `get_random_slice` function should only be called by the owner via the `register_pizza_lover` function. Also, the `owner` is trusted and will not choose a specific time for a new user to register. Therefore, I disagree with the claim of most reports in this group that an attacker can manipulate the random number of pizza slices. But I agree with the root cause of the reports in this group, that the random distribution is not completely random.

Support

FAQs

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