This is challenge number 3 from the EthereumHacker.com list of challenges. Before you can tackle any of the challenges, you first have to install and configure Metamask. If you have not already done so, please take a look at the basic configuration instructions at: EthereumHacker Challenge 1 – The Gala NFT

Our third challenge – Hack the Oracle:

Go to EthereumHacker.com and click on the second challenge: “Hack The Oracle

Your task:

In the whale wallet we discovered in the previous challenge, we found an ERC20 token, which seems to be a CBDC (central bank digital currency).

Here is the address of the token: 0x094251c982cb00B1b1E1707D61553E304289D4D8

We need to add our wallet address to the list of oracles using the “addOracles” function (see contract code below).

But, in order to successfully execute the function we need to know the secret. We know that the secret is a single word all lowercase and that it should be relatively easy to crack.

Solving the challenge:

Let’s take a look at the ERC20 token…

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract CBDC is ERC20 {
    uint256 public initialSupply = 1000000000000 ether; // 1 trillion & 18 decimals
    uint256 public inflation = 100000000 ether;
    address public centralBank;
    uint256 public usdPrice = 100000;
    mapping(address => bool) public oracles;

    constructor() ERC20("Central Bank Digital Currency", "CBDC") {
      _mint(msg.sender, initialSupply);
      centralBank = msg.sender;
    }

    function addOracle(string calldata _secret) public {
        // https://twitter.com/CentralBankDigi
        bytes32 answer = 0x70b00831d459e9f2e4b22b203fbdfd5d1830d5c16a36579bcbb12f91de2159e9;
        bytes32 yourAnswer = keccak256(abi.encode(_secret));
        require(yourAnswer == answer, "You Will Never Break In");
        oracles[msg.sender] = true;
    }

    function testAnswer(string calldata _secret) public pure returns (bytes32) {
        bytes32 yourAnswer = keccak256(abi.encode(_secret));
        return yourAnswer;
    }

    function isOracle(address _checkAddress) public view returns (bool) {
        return oracles[_checkAddress];
    }

    function printMoney() public {
        require(oracles[msg.sender] == true, "Only The Elite Can Print Money");
        _mint(centralBank, inflation);
    }

    function updatePrice(bytes32 _blockHash, uint256 _usdPrice) public {
        require(oracles[msg.sender] == true, "Only The Elite Can Manipulate Markets");
        uint256 blockNumber = block.number - 1;
        bytes32 blockHash = blockhash(blockNumber);
        require(blockHash == _blockHash, "Only Smart Contracts Can Manipulate Markets");
        usdPrice = _usdPrice;
    }

}

You can also take a look at the contract on Etherscan:  https://goerli.etherscan.io/address/0x094251c982cb00B1b1E1707D61553E304289D4D8#code

This is a standard ERC20 token that inherits from the OpenZeppelin ERC20 contract. An initial supply of 1 trillion tokens have been minted during contract deployment.

During each QE event (quantitative easing) 100 million tokens are created when the “printMoney” function is called.

Adding our address to the list of oracles:

We need to call the “add Oracle” function and provide the correct secret. Let’s take a closer look at the function:

First, it encodes (using abi.encode) the provided secret and then it takes the keccak256 hash of that value, which returns the result as bytes32. The function then compares the result with the hardcoded answer (0x70b00…)

What about this strange expression:

bytes32 yourAnswer = keccak256(abi.encode(_secret));

You see this quite often in Solidity. keccak256 is the standard Solidity hashing function (similar to SHA256 in Bitcoin) and it requires a bytes32 as argument. So, we can’t just pass our _secret (which is a string) to the hash function, we first need to convert it to bytes32 and a common way to do that is encoding the string into bytes32 using the abi.encode function.

Ok, let’s crack the code. We are looking for a single all lowercase word that’s related to the CBDC (central bank digital currency). We just try different words using the “testAnswer” function.

Go to the “Read Contract” page of the CBDC token and call the “testAnswer” function with different values for the secret: https://goerli.etherscan.io/address/0x094251c982cb00B1b1E1707D61553E304289D4D8#readContract

Let’s try the following words for the secret: central, currency, bank… and compare the result with the correct hardcoded answer: 0x70b00831d459e9f2e4b22b203fbdfd5d1830d5c16a36579bcbb12f91de2159e9

Ok, the word “bank” matches the hardcoded answer – we found the required secret word that will allow us to add our wallet address to the list of oracles.

You can go to the “Write Contract” page on Etherscan and call the “addOracle” function with the word bank as argument.

To verify if your account address has been added to the list of oracles, go to the “Read Contract” page and call the “IsOracle” function with your account address, this should return true.