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

The randomness is not really random

Root + Impact

The randomness in lines 124 & 125 are obtained insecurely/awkwardly. This exact issue has been addressed on the aptos documentation page (https://aptos.dev/build/smart-contracts/randomness)

Description

  • The intention is to generate a random number to make the competition as fair as possible to all the participants, so users can randomly get a slice between 100-500

  • The problem is that the "randomness" is predictable since it comes from the block timestamp

#[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);
} // lines 120 - 127

Risk

Likelihood: Highly likely because it determines how many slices you will get, so anyone aware of this issue will likely try to exploit it (including myself yes. I would stoop that low for pizza)

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

since the randomness is obtained from the block timestamp, it eliminates the randomization element and it is predictable

  • Reason 2

The competition wouldn't be fair anymore

Impact: High Impact

  • Impact 1

a malicious user may bias the result by picking the transaction submission time

  • Impact 2

a malicious validator can bias the result easily by selecting which block the transaction goes to

P.S: both impacts are taken straight from the horse's mouth, exactly as worded on the aptos documentation page.

Proof of Concept

A malicious user can compare results and call the function over and over until they get 500 slices instead of whatever is assigned to them on random

  • First let's run a local node

aptos node run-local-testnet --with-faucet
  • Then we initialize an account

aptos init --profile user --network local
  • After that, we deploy the contract

aptos move publish --profile user --named-addresses pizza_drop=user --skip-fetch-latest-git-deps //skipping dependencies updates because it's not working for me
  • Run the claim function twice in a row, back to back

aptos move run --profile user --function-id "0x6eef9ca6364aae9c99c06e9749a7c7170f4173834ad3ed8237bb7aa7b580472d::pizza_drop::get_random_slice"
aptos move run --profile user --function-id "0x6eef9ca6364aae9c99c06e9749a7c7170f4173834ad3ed8237bb7aa7b580472d::pizza_drop::get_random_slice"

First result is 137 and the second result is 138, this clearly shows a pattern connected to the time factor since if you increased the time gap between the function calls, the gap between the slices increases with it.

Very predictable and allows me and anyone else to wait for the perfect time to gain the maximum amount of slices

Recommended Mitigation

The best way to achieve randomness as securely as possible is to use aptos' randomness API

- remove this code
- let time = timestamp::now_microseconds();
- let random_val = time % 401;
- let random_amount = 100 + random_val;
+ add this code
#[randomness] // is a required attribute to enable the API call at runtime.
+ let num_slices = 5;
+ let rand_idx = aptos_framework::randomness::u64_range(0, num_slices); //is the randomness API call that returns an u64 integer in range [0, num_slices) uniformly at random.
+ let random_amount = (rand_idx + 1) * 100;
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.