Skip to main content

Set contract storage

Description

Smart contract can store important information in memory which changes through the contract's lifecycle. Changes happen via user interaction with the smart contract. An unauthorized set contract storage vulnerability happens when a smart contract call allows a user to set or modify contract memory when he was not supposed to be authorized.

Exploit Scenario

In this example we see that any user may access the SetContractStorage() function, and therefore modify the value of the internal counter.

#[contractimpl]
impl SetContractStorage {
/// Increment an internal counter; return the new value.
pub fn increment(env: Env, user: Address) -> u32 {
let storage = env.storage().instance();
let mut count: u32 = storage.get(&user).unwrap_or_default();
count += 1;
storage.set(&user, &count);
storage.extend_ttl(100, 100);
count
}
}

The vulnerable code example can be found here.

Remediation

Arbitrary users should not have control over keys because it implies writing any value of a mapping, lazy variable, or the main struct of the contract located in position 0 of the storage. To prevent this issue, set access control and proper authorization validation for the SetContractStorage() function.

For example, the code below, ensures only the authorized users can call SetContractStorage().

#[contractimpl]
impl SetContractStorage {
/// Increment an internal counter; return the new value.
pub fn increment(env: Env, user: Address) -> u32 {
user.require_auth();
let storage = env.storage().instance();
let mut count: u32 = storage.get(&user).unwrap_or_default();
count += 1;
storage.set(&user, &count);
storage.extend_ttl(100, 100);
count
}
}

The remediated code example can be found here.