Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Anyone can read the password from storage

Summary

For the PasswordStore contract, the assumtion of the developer as per the NatSpec, is that only the owner can read the password. The password is set in storage with a state visibility of private but anyone can access the variable's state by reading the contract state which is unsafe as passwords are sensitive data and negates this assumtion.

Vulnerability Details

The PasswordStore contract stores the password in a storage variable named s_password with a state visibility of private on line 14 of PasswordStore.sol. A variable with a state visibility of private means that the variable can only be acessed within the contract it is defined. This means that an inheriting contract cannot modify or read the variable. However, all data on the blockchain can be read from outside the blockchain through the contract storage. This means that the position of the password in storage can be determined and then the value read from the blockchain.

Since the state visibility doesn't prevent others from accessing the variable from storage, anyone, including a malicious actor, can access the owner's password by reading the contract storage.

Impact

The password is marked as private but the data is still able to be read from outside the blockchain.

The NatSpec states: This contract allows you to store a private password that others won't be able to see. Since others will be able to view the password by inspecting the storage, the password is not secure and therefore vulnerable to being stolen.

The context of this contract is unknown, and hence the true impact is unknown. For example, if the contract is to be used as an on-chain password manager, securing the password to be inaccessible to others is crucial.

Since only the owner being able to read the password is the core assumtion, and functionality that does not hold, this is a high severity vulnerability.

Proof of Concept

To read the password, the contract first needs to be deployed.

Using the local testnet node anvil to deploy and inspect the contract:

$ anvil

Running the DeployPasswordStore.s.sol script deploys the contract and sets the password to "myPassword":

$ forge script --broadcast --rpc-url "http://127.0.0.1:8545" --private-key
<private-key> script/DeployPasswordStore.s.sol

Anvil logs output:

Transaction: 0xf56a0b0a583e8a3c7650c7764c13589110a593cdbd8984b686c62db2c34d6c8b
Contract created: 0x5fbdb2315678afecb367f032d93f642f64180aa3
Gas used: 295344

By inspecting the contract, it can be seen that the storage slot of the password variable is slot 1. For completeness, forge is used to inspect the contract storage and to confirm this. This shows that the password is stored at storge slot 1:

$ forge inspect PasswordStore storage --pretty
inspect PasswordStore storage --pretty
| Name | Type | Slot | Offset | Bytes | Contract |
|------------|---------|------|--------|-------|-------------------------------------|
| s_owner | address | 0 | 0 | 20 | src/PasswordStore.sol:PasswordStore |
| s_password | string | 1 | 0 | 32 | src/PasswordStore.sol:PasswordStore |

Reading storage slot 1 of the contract yields the following output:

$ cast storage 0x5fbdb2315678afecb367f032d93f642f64180aa3 1 --rpc-url "http://127.0.0.1:8545"
// output
0x6d7950617373776f726400000000000000000000000000000000000000000014

The output can then be converted to a human-readable ASCII string:

$ cast --to-ascii 0x6d7950617373776f726400000000000000000000000000000000000000000014
// output
myPassword

The password that was set using the script was successfully read from storage and hence, non-owners will be able to read it.

Recommended Mitigation

Do not store sensitive information on-chain, unless encrypted. If sensitive information is required to be stored on-chain, the data needs to be asymetrically encrypted (public-key cryptography) which allows the data to be encrypted with a private key. Only the owner of the corresponding private key can then decrypt the data. Common examples of asymmetric encryption include Rivest-Shamir-Adleman (RSA) and Elliptic Curve Cryptography (ECC).

Tools Used

  • Forge - to run the deployment script and determine the storage position to inspect.

  • Cast - inspect the storage and decode the output.

  • Anvil - to deploy the contract in order to inspect the storage.

Updates

Lead Judging Commences

inallhonesty Lead Judge
almost 2 years ago
inallhonesty Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-anyone-can-read-storage

Private functions and state variables are only visible for the contract they are defined in and not in derived contracts. In this case private doesn't mean secret/confidential

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.