Hack The Box: TwoMillion —Walkthrough (Guided Mode)

Jasper Alblas
13 min readJul 19, 2023

Hi! It is time to look at the TwoMillion machine on Hack The Box. This machine is currently free to play to promote the new guided mode that HTB offers on retired easy machines.

The TwoMillion starting page

I am making these walkthroughs to keep myself motivated to learn cyber security, and ensure that I remember the knowledge gained by HTBs machine.

Join me on learning cyber security. I will try and explain concepts as I go, to differentiate myself from other walkthroughs.

Machine URL: https://app.hackthebox.com/machines/TwoMillion

Task 1. How many TCP ports are open?

It’s time to get started. Let’s start up a Pwnbox or if you prefer connect to the machine by using OpenVPN.

To find out the open ports we can use Nmap. Use the following command:

nmap -sV -sC -v <target ip>

The argument -sV does version detection, -sC runs some basic scripts, while -v adds some more logging. This should be enough to get started.

Nmap results showing two open ports

We got two open ports: port 22 running a OpenSSH (version 8.9p1) service, and port 80 running HTTP (Hypertext Transfer Protocol). It is the default port used to send and receive unencrypted web pages.

But you will notice the page is redirected to http://2million.htb. This is also the case if you browse to the ip address in your browser. To make this work we need to add the ip address and alias to the etc/hosts file.

sudo nano /etc/hosts
Editing the /etc/hosts file

Now we can try scanning again with nmap, this time using the alias:

nmap -sV -sC -v 2million.htb
More complete nmap results

Now the results show more info about the web page. But anyway, this does not change the number of open ports. So the anwer is still two :)

  1. How many TCP ports are open?

Answer: 2

2. What is the name of the Javascript file loaded by the ‘/invite’ page that has to do with invite codes?

It seems like we are guided towards looking at the homepage. Before doing that though, I will have a quick look at the OpenSSH version, and whether it has vulnerabilities.

We can use searchsploit for this. Simply write:

searchsploit openssh
Searching for openssh exploits

It seems like the version is quite safe, as all vulnerabilities refer to lower version numbers.

Let’s have a look at the website instead. Visit 2million.htb in your browser.

2million.htb homepage

The first thing you should always do is have a quick look around on the page. Keep scrolling down until you reach the join section.

The join section

If you press the “Join HTB” button you will get sent to a invite page.

The invite page

We find an interesting message here, taunting us to find a way to hack in.

We lack a invite code, and we need have not found anything else that can be useful. There are three things we can do:

  1. Run GoBuster to find files/directories.
  2. Look at the robots.txt file
  3. Look at the source code

At this point I recommend running gobuster, which can find directories and file in websites. While this scan is running we can have a look at the robots.txt file and source code.

Let’s run gobuster by entering:

gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://2million.htb -b 301

We need to add the -b flag to make sure the scan starts up even when meeting a status code 301. Now the results come in:

Running gobuster on the web server

Finally, another thing we could try is looking for a robots.txt file, which is a file to be read by search crawling with instructions on which pages/files the search crawlers are allowed or not allowed to crawl.

When we visit 2million.htb/robots.txt though we get a 404, meaning the file does not exist (on that location at least).

Now, let’s have a look at the source code. On the homepage nothing of interest can be found, but on the previously found invite page we find the following:

Invite page source code

If you read the inline script, you can hopefully see the security flaw. If you enter the correct invite code, you get redirected to the /register page.

Registration form

Nothing stops us from going there manually.

Anyway, we found the answer to task 2 on the screenshot earlier. Two functions are loaded in the source code, and one of them is called inviteapi.min.js.

2. What is the name of the Javascript file loaded by the ‘/invite’ page that has to do with invite codes?

Answer: inviteapi.min.js

3. What JavaScript function on the invite page returns the first hint about how to get an invite code?

This one we can answer quickly by looking at the Javascript file found during the previous task.

Inviteapi.min.js

Even though the code is minified, there are some function-name-sounding-variable-names, including ‘makeInviteCode’.

3. What JavaScript function on the invite page returns the first hint about how to get an invite code?

Answer: makeInviteCode

Task 4. On putting a valid code into the form on `/invite`, to what URL path is the browser redirected?

We answered this before by looking at the source code of the /invite page.

The source code of the invite page

In the above screenshot we can clearly see that we get redirected to /register.

4. On putting a valid code into the form on `/invite`, to what URL path is the browser redirected?

Answer: /register

5. What is the path to the endpoint the page uses when a user clicks on “Connection Pack”?

I feel like that the way forward is getting us registered as a user. But for this we need an Invite Code. Let’s have a closer look at that makeInviteCode function we noticed earlier.

I used a Javascript beautifier to make the code more readable, as it is minified now.

Beautifying the JS code

It seems like we can make a POST request to /api/v1/invite/how/to/generate. Let’s do this with curl in the terminal:

curl -X POST 2million.htb/api/v1/invite/how/to/generate

That worked. We get the following response:

Making a post request to the how-to-generate endpoint

It seems to hint at the fact that the data is encrypted. The returned object also have a property ‘enctype’ with the value ‘ROT13’. That must be the encryption method. A quick google tells us that it is a very simple encryption method in which every characters is shifted 13 characters.

To solve this we can use the tr command in the terminal.

echo 'Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb \/ncv\/i1\/vaivgr\/trarengr' | tr '[A-Za-z]' '[N-ZA-Mn-za-m]'

This translates every character between A-Z to N-Z and A-M, both for lower and upper case. We get the following result:

In order to generate the invite code, make a POST request to \/api\/v1\/invite\/generate

We know what to do now, let’s curl it!

curl -X POST 2million.htb/api/v1/invite/generate

YAY! We got a key:

NFdMUjQtMDkwRUotTDVEM0UtV0NGWUE=

Generating an invite code through curl

Let’s register a user and enter the code. When we try to register a user we can’t seem to fill out the coupon code. If you have a look at the source code we can see that the input is readonly.

Input is read only

We can simply remove this through the inspector tab on your browser’s developer tools. Remove the readonly property and you can fill out the code:

Filled out invite code

NOTE: Simply enter the page through the join link will also make the input read only! I made it more difficult..

Enter the remaining details and try to register! It fails! :(

FAILURE

When we got the invite code from the API, it mentioned a format “encoded”. It does seem to look like a base64 string.

echo NFdMUjQtMDkwRUotTDVEM0UtV0NGWUE= | base64 --decode
Decoding the base64 string

We get the decoded code:

4WLR4–090EJ-L5D3E-WCFYA

Let’s try registering again!

We’re in:

We got access to the page

Look a bit around, and in the navigation menu under Labs -> Access you will find the following section.

Connection pack button

The “Connection Pack” button sounds like the one the task mentions. Looking at the source code we can see the path to the endpoint.

The endpoint related to the connection pack

5. What is the path to the endpoint the page uses when a user clicks on “Connection Pack”?

Answer: /api/v1/user/vpn/generate

6. How many API endpoints are there under `/api/v1/admin`?

Pfew, that took a while! Now we need to find out some more information about the API.

If you visit http://2million.htb/api you see the following:

/api/v1 endpoint

If we call this /api/v1 endpoint in the browser we get redirect to a 404 page straight away. But if we call the /api/v1 endpoint in Burp we get a list of the following API endpoints:

List of different endpoints

We got enough info for the next question.

6. How many API endpoints are there under `/api/v1/admin`?

There are 3 endpoints under /admin. One GET, one POST, and one PUT endpoint.

Answer: 4…. I am quite sure it is 3! But for some reason HTB only accepts 4 for now.

7. What API endpoint can change a user account to an admin account?

Before moving on I noticed the /api/v1/admin/auth endpoint to see if a user is an admin. Let’s see if we are admin.

Checking if a user is admin through the endpoint

Well we’re not. Expected but it was worth a try.

The other interesting endpoint is found at /api/v1/admin/settings/update, which is a PUT endpoint. Maybe we can make ourselves admin this way?

If we call the previously mentioned endpoint (/api/v1/admin/settings/update/) in Burp we get the following response:

Running the admin/settings/update endpoint

Most APIs expect a request content type of type JSON, so let’s add that:

Now with the JSON content-type

Now we got closer, but we of course need to add the email to our request, otherwise the API does not know which user to edit.

Let’s do this:

Now with email!

If this was giving you trouble, remember you need an empty line between the headers and the request body (the JSON).

Now we are missing a is_admin parameter. This sounds like a boolean variable, so let’s set it to “True”.

We need to set the is_admin key to 0 or 1.

Well, it needs to be set to 1. Let’s try again!

PUT /api/v1/admin/settings/update HTTP/1.1
Host: 2million.htb
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=poj1gueqghtk53t3qeio4572kd
Content-Type: application/json
Content-Length: 54

{
"email":"jasper@jasper.com",
"is_admin": 1
}
Now with the is_admin key properly set

We’re admin now!

This is confirmed by the /api/v1/admin/auth endpoint.

Confirming our admin powers.

We can confirm this is the answer to this task.

7. What API endpoint can change a user account to an admin account?

Answer: /api/v1/admin/settings/update

8. What API endpoint has a command injection vulnerability in it?

Well, we need an API that will require some input from us, and that also needs to access the underlying system. /api/v1/admin/vpn/generate seems to be the obvious choice!

And sure enough when we add a JSON in the body, with a key-value pair with “username” as key and “adf; ls” as value you will see a list of directories. The semicolon simply ends the line, and the system will proceed to run the commands after it.

Listening directories through command injection

This is interesting, as we can now read certain files:

Reading the Database.php file through command injection

But anyway, we got our answer:

8. What API endpoint has a command injection vulnerability in it?

Answer: /api/v1/admin/vpn/generate

9. What file is commonly used in PHP applications to store environment variable values?

As a PHP developer for many years ago, I know that environment variables are often stored in a .env file in the root directory of the application.

Let’s try another command injection, but not with the command “ls -la” as username.

Listing all files and directories through command injection

This lists all files and directories, including those that are hidden. And there it is, the .env file!

Now we can read it:

Reading the .env file through command injection

And voila! We found database credentials and its name.

DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123

9. What file is commonly used in PHP applications to store environment variable values?

Answer: .env

10. Submit the flag located in the admin user’s home directory.

So now we got some credentials, but we still need some kind of shell to gain access.

We could try to get some reverse shell by fetching a payload from our computer, through command injection. But remember, there was also an SSH service. We could get VERY lucky if we try to login to the machine through SSH with the found username and password.

Logging into the SSH service
We got access!

WOW. It actually worked!

Now let’s find that flag in the admin user’s home directory.

Finding the admin flag

10. Submit the flag located in the admin user’s home directory

Answer: b06135c0f9d98e87a5f53b6549f9e9b7

11. What is the email address of the sender of the email sent to admin?

Onwards and upwards!

When logging on we get this strange message about an email. After some googling I found that this means there is a message in your spoof file, which is a generated system message by your Linux mail service.

It can be read with the following command:

less /var/mail/admin
Reading the spoof file

The email mentions major CVEs related to the Linux Kernel. Let’s look at that in a moment.

For now, we can answer this task’s question.

11. What is the email address of the sender of the email sent to admin?

Answer: ch4p@2million.htb

12. What is the 2023 CVE ID for a vulnerability in that allows an attacker to move files in the Overlay file system while maintaining metadata like the owner and SetUID bits?

We can find out about the kernel version by running:

uname -r
Readinf the kernel version

Let’s do some googling on the OverlayFS / Fuse vulnerability that can affect the kernel version. I quickly found this article:

The CVE for this vulnerability is CVE-2023–0386, and it allows unprivileged users to escalate their privileges to the root user. It also mentions that a system is likely to be vulnerable if it has a kernel version lower than 6.2. Perfect! Just what we need!

12. What is the 2023 CVE ID for a vulnerability in that allows an attacker to move files in the Overlay file system while maintaining metadata like the owner and SetUID bits?

Answer: CVE-2023–0386

13. Submit the flag located in root’s home directory.

I guess we know what to do. This repository mentions how to exploit this CVE:

We need to download the files, and transfer them to the target machine. Afterwards we can unzip the files, and run them.

Download the repository as a zip file, and afterwards transfer the files with the following command:

scp CVE-2023-0386-master.zip admin@2million.htb:/tmp/

Now do a simple ls to confirm the presence in the /tmp directory.

We received the exploit on the target pc

Now we have to unzip the file by running:

unzip CVE-2023-0386-master.zip

Now cd into the unzipped folder, and run the following to build the exploit:

make all
Building the exploit

Finally open two terminals to run the following:

# In the first terminal run:

./fuse ./ovlcap/lower ./gc

# In the second terminal type

./exp

1.

Running the first command

2.

Running the second command

We got root! HURRAY.

Now all that is left is to read the flag in the home directory.

cd /root
cat flag.txt
Reading the root flag

13. Submit the flag located in root’s home directory.

Answer: 666d01cd37d725b3a1907e4b8686116d

Another machine done, great job!

This was great fun! We utilised a lot of common techniques such as network mapping with nmap, webserver enumeration with gobuster, Burp, command injection and exploiting vulnerabilities.

Like my articles?

You are welcome to give my article a clap or two :)
I would be so grateful if you support me by buying me a cup of coffee:

Buy me a coffee

I learned a lot through HackTheBox’s Academy. If you want to sign up, you can get extra cubes, and support me in the process, if you use the following link:

Happy Hacking!

--

--

Jasper Alblas

35 year old Dutchman living in Denmark. I blog about Cyber Security. Feel free to contact me at @JAlblas on LinkedIn and X, or at https://www.jalblas.com