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

The password can be extracted by any Actor

Summary

The password can be extracted by any person familiar with the storage layout of the EVM.

Impact

High, compromising the functionality of the protocol

Tools Used

Tools from the smart contract framework foundry (cast, anvil)

Vulnerability Details

By analyzing the storage for the example deploy script:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.18;
import {Script, console2} from "forge-std/Script.sol";
import {PasswordStore} from "../src/PasswordStore.sol";
contract DeployPasswordStore is Script {
function run() public returns (PasswordStore) {
vm.startBroadcast();
PasswordStore passwordStore = new PasswordStore();
passwordStore.setPassword("myPassword");
vm.stopBroadcast();
return passwordStore;
}
}

We can see that our password is 'myPassword', if we deploy the contract following the README of the project:

  1. Start a local node

make anvil
  1. Deploy

This will default to your local node. You need to have it running in another terminal in order for it to deploy.

make deploy

If the smart contract code is public in etherscan, we can analyze the code:

contract PasswordStore {
error PasswordStore__NotOwner();
address private s_owner;
string private s_password;

The two first storage variables are the address (20 bytes) that occupies the first slot (0), the second is the string variable is located in the second slot (1), strings and bytes are stored in different ways as other data types in the EVM.

Reference to the solidity documentation about the storage layout of those datatypes:

The storage slot where the data of a string is stored is determined by simple rules, and thus predictable.

In the case that we have in our script above, the way we can extract the data is the following:

cast storage {addressOfContractDeployed} --rpc-url {AnvilURL} {StorageSlot}

In my case the command was this:

cast storage 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 --rpc-url http://127.0.0.1:8545 1

And we get this result from the command:

0x6d7950617373776f726400000000000000000000000000000000000000000014

The data is encoded in bytes, but we can easily decoded with this command:

cast --to-ascii 0x6d7950617373776f726400000000000000000000000000000000000000000014

And gives us the output:

myPassword

In the case of the password being longer, in the storage slot would only be the length of the string, following the Solidity documentation, we can calculate how many storage slots will the string be, and calculate the starting storage slot, with the keccak hash of the storage position.

Recommendations

Do not pursue deploying in Ethereum for private data, the structure of the blockchain is not designed for this kind of data, I'll recommend if the project needs a decentralized computing environment and interactions with smart contracts, to investigate Oasis Network and their blockchain Sapphire, a EVM compatible blockchain able to store private data, impossible to retrieve if the developer desires.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 2 years ago
inallhonesty Lead Judge about 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.