This is challenge number 6 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 final challenge – Graduation:

Go to EthereumHacker.com and click on the sixth challenge: “Graduation

Your task:

You need to decode a secret pharse and add your name to the Ethereum hacker leaderboard.

Here is the leaderboard contract: 0x8ef50fadf9Bd73E44Fc6839D40854F57F0C46Fc9

Make sure you finished Challenge 5, because you need the CBDC token from the last challenge. Also, the name you provide for the leaderboard must be less than 32 characters.

Solving the challenge:

Let’s take a look at the Leaderboard contract code:

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

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

interface ICBDC is IERC20 {
  function addOracle(string calldata _secret) external;
}

interface IMultisig {
  function upgradeUSDC(address _usdc) external;
  function updateCentralBank(address _newBank) external;
  function buyFundsPublic() external;
}

contract TransferCBDCTokens is ERC20 {
    address public CBDCContractAddress = 0x094251c982cb00B1b1E1707D61553E304289D4D8;
    address public multisigContractAddress = 0x550714e1Fd747084Fc5cB2B2e3a93512972aeBdA;
    address public myWalletAddress;

    constructor(address _myWalletAddress) ERC20("Transfer Token", "TT") {
        myWalletAddress = _myWalletAddress;
        _mint(address(this), 10000000000000);
    }

    function transferCBDC() public {
        ICBDC(CBDCContractAddress).addOracle("bank");

        IMultisig(multisigContractAddress).updateCentralBank(address(this));
        IMultisig(multisigContractAddress).upgradeUSDC(address(this));

        _approve(address(this), multisigContractAddress, 1000000000000);
        IMultisig(multisigContractAddress).buyFundsPublic();

        ICBDC(CBDCContractAddress).approve(address(this), 1);        
        ICBDC(CBDCContractAddress).transferFrom(address(this), myWalletAddress, 1);
    }
}

In order to get added to the Leaderboard, we need to call the “graduate” function with the correct value for _secret.

The function first verifies if the caller (msg.sender) – which will be your external MetaMask wallet – holds at least 1 CBDC token. If you completed Challenge 5, this should be fine.

Next, we have an assembly block that compares your _secret with a hardcoded bytes32 value. This code may look a bit confusing, but it is not that difficult to understand and it actually does not make much sense – it is only there to confuse the aspiring hacker.

The code first stores a hardcoded bytes32 value in memory at the address 0x80, using the EVM “mstore” opcode. EVM memory consists of a series of slots, each holding 32 bytes. The first 4 slots (0x0, 0x20, 0x40, 0x60) are reserved and that’s why we store our value at slot #5, which starts at 0x80. 4 x 32 bytes = 128 bytes, which corresponds with the hexadecimal value of 0x80.

Then, we read the same value from memory at the address 0x80, using the “mload” opcode.

Next, we retrieve the value for _secret we provided as function argument (string memory = reference type). The string value is encoded as bytes32 in memory, so we can directly compare it with the hardcoded secret using the “eq” opcode.

Careful, to retrieve our _secret from memory using the mload opcode, we need to add an offset of 32 bytes to the memory address to which _secret is pointing to. We do so by using the “add” opcode.

So, all that’s left to do is to convert the hardcoded bytes32 value (secret) to a string. Then, we can call the “graduate” function with that string and our name will be added to the leaderboard.

That’s actually relatively easy. We know, we can convert from bytes to string (which are both special arrays) using a simple cast. So, all we need to do is write a function that takes a bytes32 argument – the hardcoded secret, we are trying to convert into a string.

Then, we simply read (byte by byte) that bytes32 value into a bytes type. Once we have our bytes value, we can cast it into a string type and return the result. And, finally we call the “graduate” function with that value.

Here is the code:

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

contract FindSecret {
    
    function getStringFromBytes(bytes32 someBytes) public pure returns (string memory) {
        bytes memory bytesArray = new bytes(32);
        for (uint256 i; i < 32; i++) bytesArray[i] = someBytes[i];
        return string(bytesArray);
    }
}