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

Password can be retrieve by anyone through storage slot

Summary

The password is stored inside the s_password private string variable and should be retrieved only by the owner and using only using the getPassword function. But, as private variables in Solidity aren't really private, and the stored password isn't encrypted, anyone can retrieve the password through storage slot.

Vulnerability Details

Private variables inside Solidity aren't really private. They can't be externally called by their names, but we can retrieve their values by getting values directly from the storage slot.

The s_password is a private variable and can be retrieved following the described way above.

Proof of Concept

Paste the following code inside the PasswordStoreTest contract located at test/PasswordStore.t.sol file:

// https://ethereum.stackexchange.com/a/59335
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
function test_retrieve_password_from_storage() public {
vm.startPrank(address(1));
bytes32 passwordFromSlot = vm.load(address(passwordStore), bytes32(uint256(1)));
string memory passwordString = bytes32ToString(passwordFromSlot);
vm.stopPrank();
vm.startPrank(owner);
string memory actualPassword = passwordStore.getPassword();
assertEq(bytes(passwordString).length, bytes(actualPassword).length);
assertEq(keccak256(abi.encodePacked(passwordString)), keccak256(abi.encodePacked(actualPassword)));
}

Then run forge test --match-test test_retrieve_password_from_storage. We can see that the test run successfully as it doesn't revert.

The code above:

  • Impersonates the address(1) which is not the contract owner

  • Retrieves the s_password value from the storage slot 1 and store inside the passwordFromSlot variable

  • Converts passwordFromSlot from bytes32 to string memory and store inside the passwordString variable

  • Impersonates the owner address of the PasswordStore contract

  • Retrieves the stored password using the getPassword function and store in the actualPassword variable

  • Check if both passwordString and actualPassword variables length are equal

  • Check if both passwordString and actualPassword variable hashes are equal

Impact

A malicious user can get the password without authorization, leading to a password leak.

Tools Used

Visual Studio Code and Foundry test.

Recommendations

Encrypt the password stored in the s_password variable when set through setPassword function and decrypt when retrieved using getPassword function if you really need to store password on-chain.

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.