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

Predictable Randomness from Timestamp

Weak Randomness Source + Gaming System for Larger Pizza Slices

Description

  • The get_random_slice function is designed to provide random pizza slice sizes to users during registration, ensuring fair distribution of rewards.

  • The function uses timestamp::now_microseconds() as the randomness source instead of utilizing the secure randomness provided by the Aptos framework, as explained in the documentation, making the "random" values predictable and exploitable.

#[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

Likelihood:

  • A malicious user may bias the result by picking the transaction submission time;

  • A malicious validator can bias the result easily.

Impact:

  • Unfair distribution of pizza slices.

Recommended Mitigation

Something like this would work:

diff --git a/sources/pizza_drop.move b/sources/pizza_drop.move
index 2ca10e2..e046899 100644
--- a/sources/pizza_drop.move
+++ b/sources/pizza_drop.move
@@ -3,10 +3,12 @@ module pizza_drop::airdrop {
use aptos_framework::account;
use aptos_std::table::{Self, Table};
use aptos_framework::event;
- use aptos_framework::timestamp;
use aptos_framework::coin::{Self, Coin};
use aptos_framework::aptos_coin::AptosCoin;
+ #[test_only]
+ use aptos_std::crypto_algebra::enable_cryptography_algebra_natives;
+
#[test_only]
use std::debug;
@@ -59,7 +61,8 @@ module pizza_drop::airdrop {
coin::register<AptosCoin>(&resource_signer);
}
- public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
+ #[randomness]
+ entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
get_random_slice(user);
@@ -117,12 +120,10 @@ module pizza_drop::airdrop {
coin::transfer<AptosCoin>(&resource_signer, to, amount);
}
- #[randomness]
- entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
+ 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)
+ // 100-500 APT (in Octas: 10^8 smallest unit)
+ let random_amount = 100 + aptos_framework::randomness::u64_range(0, 400);
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}
@@ -177,6 +178,11 @@ module pizza_drop::airdrop {
use aptos_framework::account;
use aptos_framework::timestamp;
use aptos_framework::aptos_coin;
+ use aptos_framework::randomness;
+
+ enable_cryptography_algebra_natives(framework);
+ randomness::initialize_for_testing(framework);
+ randomness::set_seed(x"0000000000000000000000000000000000000000000000000000000000000000");
// Initialize timestamp and APT for testing
timestamp::set_time_has_started_for_testing(framework);
@@ -270,6 +276,11 @@ module pizza_drop::airdrop {
use aptos_framework::account;
use aptos_framework::timestamp;
use aptos_framework::aptos_coin;
+ use aptos_framework::randomness;
+
+ enable_cryptography_algebra_natives(framework);
+ randomness::initialize_for_testing(framework);
+ randomness::set_seed(x"0000000000000000000000000000000000000000000000000000000000000000");
// Setup
timestamp::set_time_has_started_for_testing(framework);
@@ -349,6 +360,11 @@ module pizza_drop::airdrop {
use aptos_framework::account;
use aptos_framework::timestamp;
use aptos_framework::aptos_coin;
+ use aptos_framework::randomness;
+
+ enable_cryptography_algebra_natives(framework);
+ randomness::initialize_for_testing(framework);
+ randomness::set_seed(x"0000000000000000000000000000000000000000000000000000000000000000");
// Setup
timestamp::set_time_has_started_for_testing(framework);
@@ -379,4 +395,36 @@ module pizza_drop::airdrop {
coin::destroy_burn_cap(burn_cap);
coin::destroy_mint_cap(mint_cap);
}
}
Updates

Appeal created

bube Lead Judge about 1 month 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.