# Safe space, where we can be vulnerable

By [EthernautDAO](https://paragraph.com/@ethernautdao) · 2022-07-12

---

Safe space is a series of hackable smart contracts (CTF) where community members will dive deep into some solidity vulnerabilities.
-----------------------------------------------------------------------------------------------------------------------------------

Author: Giovannidisiena.eth
---------------------------

[https://twitter.com/giovannidisiena](https://twitter.com/giovannidisiena)

Also, follow Stermi.eth for another good explanation:
-----------------------------------------------------

[https://twitter.com/StErMi](https://twitter.com/StErMi)

Reading Private Data

If you have some familiarity with Solidity smart contracts then you will have come across state variable visibility – public, internal, and private. Although it sounds counter to what you might expect, the key takeaway from this challenge is that anyone can read the value stored in a private variable.

Repeat after me:

`private` **variables aren’t private**

This keyword refers only to the ability of an external contract to access the variable, while to Shadowy Super Coders th

e value is accessible via inspecting the contract storage layout and reading the corresponding slot directly.

To pull off the hack we are going to be using Foundry (big up Georgios & Co). If you don’t already have it installed then go ahead and run `curl -L https://foundry.paradigm.xyz | bash` before running `foundryup` in a new session. Additional documentation is available in the [official repo](https://github.com/foundry-rs/foundry) and [book](https://book.getfoundry.sh).

We will first create a new directory and initialize a new Forge project like so:

    mkdir private-data && cd $_ && forge init
    

Go ahead and copy the contract code to PrivateData.sol. We’ll need this to inspect the contract storage layout (and if you wanted to run tests against a local fork using `anvil`).

The easiest method for inspecting the storage layout really is as simple as:

    forge inspect PrivateData storage —pretty
    

![](https://storage.googleapis.com/papyrus_images/c4d5b2c35500964a63c2b329ed3457cf45a1f3f1fc6adbde79500a26eed439b0.png)

From this, we can see that the variable `secretKey` corresponds to storage slot 8.

To arrive at the same result without the power of Forge, you would need to apply [storage layout rules](https://docs.soliditylang.org/en/v0.8.13/internals/layout_in_storage.html#layout-of-state-variables-in-storage) manually, understanding that the EVM stores state variables in 32-byte slots and performs slot packing, such that these variables get stored in a single slot, if successive declarations sum to no more than 32 bytes:

*   `NUM` is inlined to contract bytecode at compile time since it is a constant variable
    
*   `owner` is of address type, which is 20 bytes, and occupies storage slot 0
    
*   `randomData` is a constant-size array of bytes32 and occupies 160 bytes, from storage slot 1 to slot 5
    
*   `addressToKeys` mapping has an unpredictable size, so the elements it contains are stored starting at a different storage slot that is computed using a keccak hash which is stored at storage slot 6
    
*   `a` and `b` are both of type uint128 which together pack to 32 bytes and as such both occupy slot 7
    
*   `secretHash` therefore occupies storage slot 8
    

Regardless of which method you choose, once you have the desired storage slot it is again as simple as running the following command to read the value stored:

    cast storage 0x620E0c88E0f8F36bCC06736138bDEd99B6401192 8 --rpc-url https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_GOERLI_ID}
    

![](https://storage.googleapis.com/papyrus_images/49f774723524c381b2af1f06514ae9878155643be7c294db5f5bc8e529e52542.png)

And there it is! We have the secret key.

Now to take ownership of the contract, send a transaction with:

    cast send 0x620E0c88E0f8F36bCC06736138bDEd99B6401192 --rpc-url https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_GOERLI_ID} --private-key ${PRIVATE_KEY} "takeOwnership(uint256)()" 0x43223b17c0688af05eea8efbd2b6c424174874f5945a09fce2e3fa1afb6984b7
    

And view the current owner with:

    cast call 0x620E0c88E0f8F36bCC06736138bDEd99B6401192 --rpc-url https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_GOERLI_ID} "owner()(address)”
    

Of course, you could also use our newfound superpower and read the storage slot 0 directly.

As a quick aside, it is important to note that you should not rely on block data for randomness, as explained [here](https://betterprogramming.pub/how-to-generate-truly-random-numbers-in-solidity-and-blockchain-9ced6472dbdf) and `rndString` is visible on Etherscan as a decoded constructor argument.

![](https://storage.googleapis.com/papyrus_images/de92cf98f67daf8150a7d716c184cf577bc8d91508c0c06b8d68ae5d79dac06e.png)

A slightly improved solution could be storing the keccak hash of the secret key in a commit-reveal type fashion, so as not to expose the key itself, and validating input against this; however, it is still of course flawed in that the correct key will become visible to everyone once someone crafts a successful transaction.

![](https://storage.googleapis.com/papyrus_images/590c3f1af9180f8de754ef55192175fef86e66cdcac96a0e9dc3ec7a1e0cc438.png)

I hope that was helpful – happy hacking!

---

*Originally published on [EthernautDAO](https://paragraph.com/@ethernautdao/safe-space-where-we-can-be-vulnerable)*
