Hello Ethernaut is the very first challenge in the Ethernaut series. It serves basically as an introduction to the system of the challenges and how to interact with the smart contracts etc.
The Hello Ethernaut challenge is pretty basic as it simply teaches you how to interact with the contract through your browser developer tools.
First of all, we will deploy a new contract for us to interact with by pressing Get new instance (donāt forget to create a Metamask wallet if you donāt have one):
Launch instance
This will prompt us with an authorization window from Metamask: Metamask
After we launch the instance, we must open developer tools on our browser to interact with the contract. We can do so by pressing Shift + Ctrl + I. We will find this welcome message from from the contract:
The contract address we will be interacting with is 0x9A49046252239bfc3D5c6a502f804Dd262d485e1. We can even check it from Etherscan for the Sepolia chain .
Now typing help to see what we can do:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
->help() player 'current player address' ethernaut 'main game contract' level 'current level contract address' contract 'current level contract instance (if created)' instance 'current level instance contract address (if created)' version 'current game version' getBalance(address) 'gets balance of address in ether' getBlockNumber() 'gets current network block number' sendTransaction({options}) 'send transaction util' getNetworkId() 'get ethereum network id' toWei(ether) 'convert ether units to wei' fromWei(wei) 'convert wei units to ether' deployAllContracts() 'Deploy all the remaining contracts on the current network.'
We can start off by checking the info of our contract:
1 2 3 4 5 6
->contract.info() <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "You will find what you need in info1()."
We get an indication to invoke info1() next.
Invoking info1(), we get more data from the contract:
1 2 3 4 5 6
->contract.info1() <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "Try info2(), but with \"hello\" as a parameter."
Once again, we get an indication to use info2() with hello parameter.
Now invoking info2() with hello parameter:
1 2 3 4 5
->contract.info2('hello') <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "The property infoNum holds the number of the next info method to call."
Once again, we get an indication to use infoNum() that will give us back a number.
The number we got from the infoNum() function is 42. Which should be appended to the next info we will call.
Now calling info42():
1 2 3 4 5
->contract.info42() <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "theMethodName is the name of the next method."
Now we know that theMethodName() is the next method to call.
Calling theMethodName():
1 2 3 4 5
->contract.theMethodName() <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "The method name is method7123949."
Once again we will call a new method method7123949().
Now calling method7123949():
1 2 3 4 5
->contract.method7123949() <snip> [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "If you know the password, submit it to authenticate()."
Now we are at the final step where we have to get the password and then use it as an argument for the athenticate() method.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0;
contract Instance { string public password; uint8 public infoNum = 42; string public theMethodName = "The method name is method7123949."; bool private cleared = false;
functioninfo() public pure returns (string memory) { return"You will find what you need in info1()."; }
functioninfo1() public pure returns (string memory) { return'Try info2(), but with "hello" as a parameter.'; }
functioninfo2(string memory param) public pure returns (string memory) { if (keccak256(abi.encodePacked(param)) == keccak256(abi.encodePacked("hello"))) { return"The property infoNum holds the number of the next info method to call."; } return"Wrong parameter."; }
functioninfo42() public pure returns (string memory) { return"theMethodName is the name of the next method."; }
functionmethod7123949() public pure returns (string memory) { return"If you know the password, submit it to authenticate()."; }
functionauthenticate(string memory passkey) public { if (keccak256(abi.encodePacked(passkey)) == keccak256(abi.encodePacked(password))) { cleared = true; } }
functiongetCleared() public view returns (bool) { return cleared; } }
Exploitation Steps - Locally compiled contract
Now going an extra step, we will take the provided contract source code, compile it, and exploit it locally. The exploitation steps are the same as explained above. You can find the full code under test/HelloEthernautLocal.ts (hardhat).
$ npx hardhat test test/HelloEthernautLocal.ts No contracts to compile RunningMocha tests
LocalHelloEthernautTest Info() Output: Info1() Output: Tryinfo2(), but with"hello"as a parameter. Info2() Output: The property infoNum holds the number of the next info method to call. InfoNum() Output: 42 Info42() Output: theMethodName is the name of the next method. theMethodName() Output: The method name is method7123949. method7123949() Output: If you know the password, submit it to authenticate(). password() Output: ethernaut0 ā First challenge solve (70ms)
1 passing (73ms)
Conclusion
That was it for Hello Ethernaut challenge from Ethernaut series.