Ethernaut: Hello Ethernaut

Foued SAIDI Lv5

Overview

Hello everyone, I hope you are doing fantastic!

This is Foued SAIDI (0xkujen), senior pentester and a wannabe Web3/Blockhain Security Researcher.

A few days ago I launched a blog post series where I will be posting about my journey in Web3/Blockchain security. You can read the first blog post about Damn Vulnerable DeFi challenges.

Today, I am following up on the Damn Vulnerable DeFi series and challenging myself even more by finishing the Ethernaut challenges series presented to us by OpenZeppelin !

Hope you enjoy it and learn something new!

ā€˜Hello Ethernaut’ Challenge

Hello-Ethernaut
Hello-Ethernaut

Challenge Description

Overview

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.

Link to the original challenge

Github repo link that contains challenge code and solver

Exploitation Steps - Developer Tools

  1. 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
Launch instance

This will prompt us with an authorization window from Metamask:

Metamask
Metamask

  1. 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:

Welcome message
Welcome message

1
2
3
4
5
Hello Ethernaut
Type help() for a listing of custom web3 addons
=> Level address 0x7E0f53981657345B31C59aC44e9c21631Ce710c7
(ā—*∩_∩*ā—) Requesting new instance from level... < < <<PLEASE WAIT>> > >
=> Instance address 0x9A49046252239bfc3D5c6a502f804Dd262d485e1
  1. The contract address we will be interacting with is 0x9A49046252239bfc3D5c6a502f804Dd262d485e1. We can even check it from Etherscan for the Sepolia chain .

  2. 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.'

  1. 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.

  1. 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.

  1. 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.

  1. Now invoking infoNum():
1
2
3
4
5
6
7
8
9
10
->contract.infoNum()
<snip>
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: i
<snip>
words: Array(2)
0: 42
length: 2
<snip>

The number we got from the infoNum() function is 42. Which should be appended to the next info we will call.

  1. 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.

  1. 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().

  1. 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.

  1. We can get the password by invoking:
1
2
3
4
5
->contract.password()
<snip>
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "ethernaut0"

The password is ethernaut0.

  1. Finally, we can authenticate using the password:
1
->contract.authenticate('ethernaut0')

This will prompt us a metamask window to authenticate:

Authentication
Authentication

With that, the challenge is solved by pressing submit instance and we are returned the code source of the contract itself!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 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;

// constructor
constructor(string memory _password) {
password = _password;
}

function info() public pure returns (string memory) {
return "You will find what you need in info1().";
}

function info1() public pure returns (string memory) {
return 'Try info2(), but with "hello" as a parameter.';
}

function info2(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.";
}

function info42() public pure returns (string memory) {
return "theMethodName is the name of the next method.";
}

function method7123949() public pure returns (string memory) {
return "If you know the password, submit it to authenticate().";
}

function authenticate(string memory passkey) public {
if (keccak256(abi.encodePacked(passkey)) == keccak256(abi.encodePacked(password))) {
cleared = true;
}
}

function getCleared() 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).

Here is the full code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import hre from "hardhat";
const { ethers, networkHelpers } = await hre.network.connect();


describe("Local HelloEthernaut Test", function () {

it("First challenge solve", async () => {
const Hello = await ethers.getContractFactory("HelloEthernaut");

const hello = await Hello.deploy('ethernaut0');
await hello.waitForDeployment();

const infoOutput = await hello.info();
console.log("Info() Output: ",infoOutput);

const info1Output=await hello.info1();
console.log("Info1() Output: ",info1Output);

const info2Output=await hello.info2('hello');
console.log("Info2() Output: ",info2Output);

const infoNumOutput=Number(await hello.infoNum());
console.log("InfoNum() Output: ",infoNumOutput);

const info42Output=await hello.info42();
console.log("Info42() Output: ",info42Output);

const theMethodNameOutput=await hello.theMethodName();
console.log("theMethodName() Output: ",theMethodNameOutput);

const method7123949Output=await hello.method7123949();
console.log("method7123949() Output: ",method7123949Output);

const passwordOutput=await hello.password();
console.log("password() Output: ",passwordOutput);

const authenticationOutput=await hello.authenticate('REDACTED');
console.log("authenticate() Output: ",authenticationOutput);
});
});

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ npx hardhat test test/HelloEthernautLocal.ts
No contracts to compile
Running Mocha tests


Local HelloEthernaut Test
Info() Output:
Info1() Output: Try info2(), 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.

You can find through this github link the repository that contains my solver and all the future Ethernaut solutions Inshallah!

See you next time~

  • Title: Ethernaut: Hello Ethernaut
  • Author: Foued SAIDI
  • Created at : 2025-12-15 15:38:03
  • Updated at : 2025-12-15 16:06:08
  • Link: https://kujen5.github.io/2025/12/15/Ethernaut-Hello-Ethernaut/
  • License: This work is licensed under CC BY-NC-SA 4.0.