#[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;
@> table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}
#[test(deployer = @pizza_drop, attacker = @0x123, framework = @0x1)]
fun test_unauthorized_registration_exploit(
deployer: &signer,
attacker: &signer,
framework: &signer
) acquires State, ModuleData {
use aptos_framework::account;
use aptos_framework::timestamp;
use aptos_framework::aptos_coin;
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);
get_random_slice(attacker_addr);
assert!(is_registered(attacker_addr), 1);
let assigned_amount = get_claimed_amount(attacker_addr);
assert!(assigned_amount >= 100 && assigned_amount <= 500, 2);
claim_pizza_slice(attacker);
let attacker_balance = coin::balance<AptosCoin>(attacker_addr);
assert!(attacker_balance == assigned_amount, 3);
coin::destroy_burn_cap(burn_cap);
coin::destroy_mint_cap(mint_cap);
}
Test Result: aptos move test -f test_unauthorized_registration_exploit passes, confirming the vulnerability exists.
- remove this code
+ add this code
+ const E_ALREADY_REGISTERED: u64 = 5;
- #[randomness]
- entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
+ fun get_random_slice_internal(user_addr: address, state: &mut State): u64 {
+ assert!(!table::contains(&state.users_claimed_amount, user_addr), E_ALREADY_REGISTERED);
let time = timestamp::now_microseconds();
let random_val = time % 401;
let random_amount = 100 + random_val;
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
+ random_amount
}
public 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);
+ get_random_slice_internal(user, state);
event::emit(PizzaLoverRegistered {
user: user,
});
}
Explanation: Remove the entry and #[randomness] modifiers to make the function internal-only.
Rename to get_random_slice_internal and add duplicate registration protection.
This ensures only the owner-controlled register_pizza_lover function can register users, restoring the intended access control model.