HackMyVM - Devguru
Creado
Máquina Linux nivel medio (diría que difícil le queda mejor). Directorios .git y credenciales volando, bastantes juegos con Adminer, SSTI en twig, un Gitea viejito vulnerable mediante hooks a RCE, movimientos sensuales para leer logs en el sistema y engaños contra sudo para que ejecute comandos como el usuario root pasándole el usuario #-1 😮
TL;DR (Spanish writeup)
Creada por: zayotic.
Descargamos la máquina acá: HackMyVM - Devguru.
“Tienes que revisar que lleves todo mijito. Revisa dos veces…”
Recorreremos un servidor web que está siendo mantenido por el October CMS
, encontraremos el servicio Adminer
(para jugar con bases de datos) y además una carpeta .git
con información más que útil. Entre esa información tendremos unas credenciales de bases de datos que usaremos con adminer para modificar data relacionada con October CMS. Cambiaremos las credenciales del usuario frank
y lograremos una sesión/cookie válida contra el gestor de contenido, dándonos cuenta de que podemos modificar el contenido mostrado en el servidor web.
Dentro nos encontraremos con una Inyección de Plantillas desde el lado del Servidor
(SSTI
) explotando la plantilla twig
usada por Laravel (y PHP). Con esto obtendremos una terminal remota como el usuario www-data
en el sistema.
Daremos unas vueltas por el sistema y encontraremos un backup de la configuración usada por otro servidor web (ejecutándose en el puerto 8585) que mantiene a Gitea
. En su contenido habrán unas credenciales para la base de datos que usa Gitea, jugaremos con Adminer
de nuevo para gestionar cosas yyyy modificar otras. Cambiaremos ahora la contraseña del usuario frank en Gitea para obtener una sesión en ese servicio. Esto nos servirá para darle vida a un exploit (CVE) que necesita un usuario autenticado para funcionar, romperemos los hooks
de git
y ejecutaremos comandos remotamente como el usuario frank
.
Después de generar una terminal como frank, tendremos un permiso para ejecutar como cualquier usuario menos root el binario sqlite3
(que realmente solo lo usamos para ejecutar comandos mediante su parámetro .shell COMANDO), nos moveremos con ese permiso como el usuario syslog
por el sistema para leer algunos logs y encontrar comandos ejecutados y algunos detalles más, entre ellos, tendremos al usuario #-1
involucrado en comandos sospechosos, investigaremos y entenderemos que nos enfrentamos a una versión de sudo
vulnerable a elevación de privilegios por una falla en la validación de los UID.
Así, conseguiremos una shell como el usuario root
.
…
La idea inicial de esta locura es tener mis “notas” por si algun día se me olvida todo (lo que es muuuy probable), leer esto y reencontrarme (o talvez no) 😄 La segunda idea surgio con el tiempo, ya que me di cuenta que esta es una puerta para personitas que como yo al inicio (o simplemente a veces) nos estancamos en este mundo de la seguridad, por lo que si tengo la oportunidad de ayudarlos ¿por qué no hacerlo?
Un detalle es que si ves mucho texto, es por que me gusta mostrar tanto errores como exitos y tambien plasmar todo desde una perspectiva más de enseñanza que de solo pasos a seguir. Sin menos, muchas gracias <3
…
Simplemente, hay que bailar, tienes que sentir los sonidos, ellos te abrazan.
…
Reconocimiento #
Como es usual en esta plataforma, descargamos la VM, la cargamos en el virtualizador, la iniciamos y ahora viene la parte juguetona, encontrar su IP.
En este caso usaremos la herramienta nmap
para ello.
Tengo una red NAT configurada para que toda máquina que agregue a esa red tome una IP entre el rango 192.168.100.0/24
(192.168.100.1
- 192.168.100.254
), así que hagamos un escaneo sobre ese rango a ver cuál suena llamativa:
nmap -sn 192.168.100.0/24
Parámetro | Descripción |
---|---|
-sn | Envía trazas ICMP (un ping) |
Y obtenemos el gateway de la red, mi IP y una que ni idea, así que esa debe ser la de la máquina vulnerable:
# Gateway
Nmap scan report for 192.168.100.1
Host is up (0.00026s latency).
# Mi IP
Nmap scan report for 192.168.100.7
Host is up (0.000085s latency).
# Devguru?
Nmap scan report for 192.168.100.28
Host is up (0.00021s latency).
Teniendo esa IP vamos a comenzar ahora sí, hagamos un escaneo de puertos y de ahí en la validación sabremos si tenemos la máquina víctima:
nmap -p- --open 192.168.100.28 -v -oG initScan
Parámetro | Descripción |
---|---|
-p- | Escanea todos los 65535 |
–open | Solo los puertos que están abiertos |
-v | Permite ver en consola lo que va encontrando |
-oG | Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae los puertos en la clipboard |
Obtenemos:
Puerto | Descripción |
---|---|
22 | SSH: Tenemos la posibilidad de generar terminales seguras. |
80 | HTTP: Se nos ofrece un servidor web. |
8585 | HTTP: Otro servidor web. |
+ ~ +(Usando la función extractPorts
(referenciada antes) podemos copiar rápidamente los puertos en la clipboard, en este caso no es necesario (ya que tenemos pocos puertos), pero si tuviéramos varios evitamos tener que escribirlos uno a uno
~ extractPorts portScan
[*] Extracting information...
[*] IP Address: 192.168.100.28
[*] Open ports: 22,80,8585
[*] Ports copied to clipboard
)+ ~ +
Ya conocemos los puertos abiertos, apoyémonos de nmap y digámosle que nos muestre las versiones de cada servicio y también que usé unos scripts que tiene por default a ver si ellos encuentran algo más:
nmap -p 22,80,8585 -sCV 192.168.100.28 -oN portScan
Parámetro | Descripción |
---|---|
-p | Escaneo de los puertos obtenidos |
-sC | Muestra todos los scripts relacionados con el servicio |
-sV | Nos permite ver la versión del servicio |
-oN | Guarda el output en un archivo |
Tenemos algunas cositas relevantes:
Puerto | Servicio | Versión |
---|---|---|
22 | SSH | OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) |
80 | HTTP | Apache httpd 2.4.29 |
- Uno de los scripts nos dice que encontró una carpeta
.git
, esto es interesante, lo anotamos pa verlo ahorita.
Puerto | Servicio | Versión |
---|---|---|
8585 | HTTP | Nadita, peeero: |
- Uno de los scripts nos reporta que estamos detrás de un
Gitea: Git with a cup of tea
:o Lo guardamos y seguimos.
Por el momento tenemos ya cosas para revisar, así que a darle!
Enumeración #
Empecemos descubriendo que hay detrás del sitio web sobre el puerto 80.
Recorriendo el puerto 80 📌
Una empresa informando sobre sus servicios, lo único relevante es un correo del cual podemos extraer dos cosas:
- Un posible dominio:
devguru.local
. - Un posible usuario:
support
osupport@devguru.local
.
Después de revisar el código fuente y el funcionamiento inicial del sitio, nos vamos a hacer fuzzing
, ya que lo único interesante es que está ejecutando el CMS October (que a su vez necesita de Laravel para ser ejecutado), pero nada de versiones ni na’…
ffuf -c -w /opt/seclists/Discovery/Web-Content/common.txt -u http://192.168.100.28/FUZZ
Intentando encontrar rutas que el servidor tenga alojadas, pero que no sean visibles, tenemos unas cuantas:
🗂️ Un objeto .git
, que se relaciona a algún proyecto manejado con Git, esto es crítico, ya que existen herramientas para “regenerar” todo el contenido que estuvo alojado como “proyecto”, o sea, código fuente, imágenes, commits, etc.
🛠️ También contamos con el objeto .htaccess
el cual permite customizar la configuración relacionada con el servidor, podemos revisarlo a ver que reglas o cositas tiene.
🛣️ Rutas con nombres interesantes(backend
tiene un login-panel para ingresar a la configuración de October CMS) a las cuales podemos profundizarles en busca de más archivos.
Empecemos por orden, veamos el tema de .git
.
Explotación #
¿Se leakeo un directorio .git? Veamos su pasado 📌
🗂️ Para esto podemos usar la herramienta git-dumper, es muy cómoda y sencilla de usar. La clonamos y ejecutamos para que todo el contenido del .git
nos lo guarde en la carpeta git_leaked
:
python3 ./git-dumper/git_dumper.py http://192.168.100.28/ git_leaked
Listos, ya tendríamos todo dentro de git_leaked
:
Revisando y entendiendo cosas de los objetos, concluimos:
- Los archivos que tenemos en
git_leaked
también existen en el servidor web, por lo que ese.git
hace referencia al sitio web que estamos auditando (: -
Hay unas credenciales contra la base de datos
octoberdb
(October CMS, ojo, recordemos el login-panel comentado antes) en el objetoconfig/database.php
:Guardémoslas que toman sentido con el siguiente ítem.
-
Existe un archivo muy interesante llamado
adminer.php
.Que buscando información sobre él, entendemos que funciona como
phpMyAdmin
, o sea, es un gestor/administrador de contenido relacionado con bases de datos.(Además, sabemos qué versión está ejecutando:
Adminer 4.7.7
, pero buscando vulnerabilidades relacionadas, no llegamos a nada).
Me gusta, tenemos varias cosas a probar, pero hay una clara, ¿cuál se te ocurre?
…
Contamos con Adminer el cual nos permite administrar bases de datos, tenemos unas credenciales relacionadas con una base de datos de October CMS
, puessssssss:
Damos clic en Login
y:
Liiiiistos, dentro (:
Si vamos a la fija, lo primero interesante siempre será la tabla que relacione usuarios, tenemos inicialmente backend_users
:
Perfecto, un usuario llamado frank
y su contraseña en formato hash
, podemos pensar en tomar esa contraseña e intentar romperla mediante un ataque de fuerza bruta (qué lo hice :P), peeeeero ¿y si existe la posibilidad… de que tengamos… quizás… permisos para modificar datos de la base de datos? 🤭 😝
Validemos con esa misma tabla, intentemos cambiar el apellido de frank
:
- Nos posicionamos sobre el registro a modificar.
-
En el inicio damos clic en
edit
, cambiamos el contenido:Y guardamos para obteneeeer:
EPAAALE! Pues tenemos permisos, entonces armémonos una contraseña con el mismo algoritmo que está usando el servidor y probamooooooooos (:
Según los ejemplos de hashes que tiene hashcat, contamos con un hash generado mediante el algoritmo bcrypt:
Me gusta, hay varias maneras de generar ese tipo de hashes, por ejemplo en Linux lo hacemos así:
htpasswd -bnBC 10 "" aca_va_la_password_a_hashear | tr -d ':\n'
Así que coloquemos como contraseña franksito
:
htpasswd -bnBC 10 "" franksito | tr -d ':\n'
Guardamos y al probar en el login http://192.168.100.28/backend/backend/auth/signin
las credenciales frank:franksito
…
ESTAMOS ADENTROOOOOOOOOOOOOO COMO FRANK!!
Recorramos el sitio a ver que encontramos (:
Jugamos con plantillas peligrosas 📌
Encontramos los objetos que vemos en el sitio web, por ejemplo, para ver este home:
Tenemos que seguir esta ruta:
Algo que notamos son los corchetes ({}
) al inicio y final del objeto (y en otros objetos es igual), lo cual toma sentido más adelante, pero por ahora estamos perdidos, ya que intentando inyectar código PHP
no llegamos a ningún lado :(
Buscando en internet como ejecutar código PHP en October CMS caemos acá:
En uno de los comentarios encontramos algo iluminador:
De una se me encendió una idea 💡 Esa plantilla (twig) la hemos visto cuando explotamos cositas relacionadas con Server Side Template Injection (SSTI)
, pero por lo general lo rompemos en Flask, acá estamos jugando con PHP, puede ser interesante validar si es vulnerable.
🔳 Una
SSTI
es la manera en la cual un atacante aprovecha la sintaxis de una plantilla para inyectar codigo malicioso que posteriormente sera renderizado e interpretado con ayuda de la plantilla.
Con ayuda de este recurso vamos a validar cositas:
En la derecha encontramos Twig (PHP), hacemos la validación inicial (la común en los SSTI) a ver si es interpretada, haremos que nos devuelva el resultado de multiplicar 7*7
:
{{7*7}}
Guardamos y revisamos en la vista principaaaaaaal:
PERFECTOO! Ahora sí, recordemos que mediante un SSTI podemos ejecutar código en el sistema 🤤 pongámonos serios y rompamos estoOOO 🐓
RCE mediante un SSTI en PHP 📌
Acá y acá hay varias opciones para probar, nos quedamos con:
{{['id']|filter('system')}}
Donde se esta llamando la función system() de PHP (que ejecuta programas en el sistema) para lanzar el comando
id
.
Copiamos y pegamos el payload en el objeto slider.htm
, guardamos yyyyy validamos:
Bueeeenoooo que pasoooooo!! Tenemos un error :(, pero se nos filtra una ruta interna :)
Buscando solución para ese error en internet, llegamos a este hilo donde M. Galardi
nos ofreció una ayudita hace 5 años (:
Tiene sentido, al parecer, la ejecución del comando que queremos devuelve un array, pero no estamos haciéndole un tratamiento a ese array para que pueda ser mostrado como cadena de texto, por eso hay que iterar sobre él, hagámosle:
EPAAAAAA!
Pos les dejo de tarea hacer que la máquina víctima nos genere una terminal, o mejor llamada, una reverse shell (: solo que cuando ya la tengan, háganla linda y funcional: Generate Fully TTY.
Sigamos…
Giteando : www-data -> frank #
En el sistema también existe el usuario frank
, así que entiendo que debemos movernos a él antes que a root, pero nunca se sabe.
Inicialmente, no encontramos nada usable con www-data
, pero al listar los archivos que tienen a frank
como propietario vemos locuras:
Opa, pues hay varias opciones, pero a grandes rasgos hay dos temitas, ¿ves el principal? Existe un archivo .bak
(backup) para app.ini
yyyy tenemos permisos de lectura, si nos pillamos las primeras líneas ya tiene relación todo:
www-data@devguru:/var/www/html$ cat /var/backups/app.ini.bak | head
; This file lists the default values used by Gitea
...
Gitea, Gitea, Gitea y Gitea por todos lados. ¡Démosle importancia ahora sí!
Recuerdas el puerto 8585
y que nos hablaba de Gitea
? Rápidamente:
↖️ “Gitea is a community managed lightweight code hosting solution written in Go.” ~ Gitea.io
Ya teniendo el contexto podemos seguir.
En el sistema hay varios objetos relacionados con Gitea, entre ellos uno que contiene un backup de la configuración usada:
🔀 “Any changes to the Gitea configuration file should be made in
custom/conf/app.ini
or any corresponding location.” ~ docs.gitea.io
Así que después de darle un vistazo a ese objeto encontramos:
-
Unas credenciales hacia una base de datos llamada
gitea
:Toda esta es información que nos sirve para jugar de nuevo con
adminer
(en caso de que sean válidas claramente). -
La llave secreta con la que se generan las sesiones (cookies) dentro del sitio web (de Gitea, pa evitar que se me pierdan 🐤):
Con esto podemos pensar en generar una cookie para ingresar al sitio sin necesidad de credenciales.
La cosa es que probando maneras de generar una cookie válida no llegamos a ningún lado, por lo que juguemos con las credenciales de la base de datos y Adminer…
Molestando a frank ahora en Gitea 📌
Procedemos a conectarnos con Adminer a la base de datos (a ver si son válidas):
LIIIIISTOS! Son válIdAs (: ¿Lo primero que era? Buscar una tabla que hable de usuarios…
Me gusta, ya la tenemos. Existe frank
como usuario y vemos su contraseña (además tiene sal para darle más seguridad) hasheada con el algoritmo pbkdf2
, la cosa es que intentando crackearla ni siquiera se nos logra reconocer que tipo de hash es, así que tamos F.
Peeeeero acá recordamos nuestro truco, ¿qué tal que podamos modificar la información de la base de datos? Probemos a cambiarle el lower_name
por el frank:
Tamos, contamos con permisos! Ahora a cambiar la contraseña…
Dando vueltas con ese cambio nos quedamos un bueeeeen rato, probando y probando opciones, quitando sal, modificando sal, dejando en blanco, etc. NADA, nada de nada.
💡 Entonces decidí volver al objeto con el backup, a intentar ver algo que me guiara (y si, algo me guio).
Si repasamos el apartado security
de nuevo, casi al final vemos qué algoritmos soporta Gitea para manejar los hashes:
Por default esta pbkdf2
, solo queeeeee, también acepta bcrypt
, ¿recuerdas que ya jugamos con él? 🙃
GENEREMOS UNA CONTRASEÑA CON ESE ALGORITMO Y PROBÉMOOS! Pongámosle, frankelfrank
:
Ahora en Adminer con la info de frank
:
- Colocamos la contraseña creada arriba en el campo
passwd
. - Dejamos a
bcrypt
como tipo de algoritmo usado en el campopasswd_hash_algo
. - Borramos la data que este dentro de
salt
andrands
. - Dejamos
frank
como data del campolower_name
.
Finalmente:
PERFECTO, ahora la prueba final, vamos a http://192.168.100.28:8585/user/login
, colocamos las credenciales frank:frankelfrank
yyyyyyy:
VAMOOOOOOOOOOOOOOOOOOOOOOOOOOOOOS!
De primeras no vemos ningún repositorio curioso, al parecer es la misma información que ya dumpeamos al obtener el .git
.
Algo en lo que podemos fijarnos es en la versión del Gitea, ya que quizás existan vulnerabilidades contra ella. La tomamos, buscamos y encontramos 😯:
Hay una explotación basada en los git hooks (pequeñas acciones ancladas a eventos (como commits, pushs, pulls, etc.) que pueden producir cositas curiosas al ser ejecutadas) de Git
. Un atacante (nosotros) puede aprovechar esto para mediante un repositorio crear eventos (hooks
) maliciosos para ejecutar código remotamente en el sistema 🤭
LO CHÉVERE es que si pensamos a futuro, quizás sea frank
el que esté sosteniendo Gitea en el sistema, así que si ejecutamos código, pueda que lo ejecutemos como él (:
RCE en Gitea 1.12.5 📌
Si ejecutamos ese script pasa algo internamente y no se ejecuta, pero también al revisarlo nos damos cuenta de que la explotación es bien sencilla y podemos hacerla manual (:
⚠️⚠️⚠️ Algo importante es que debemos crear un repositorio en Gitea y su hook malicioso:
- En la parte superior derecha hay un
+
. - Le damos a “nuevo repo”.
- Le colocamos un nombre y creamos.
- Arriba a la derecha dirá
Settings
, damos ahí. - Después donde dice
Git Hooks
. - Seleccionamos el lápiz que está en el hook post-receive (el cual se invoca después de hacer un push (
git push
)). - Y DENTRO COLOCAMOS EL COMANDO QUE QUEREMOS EJECUTAR EN EL RCE.
Después de eso, ahora si seguimos el script:
❓ En el punto
7
no se pierdan, solo es colocar la ruta para llegar a su repo, ejemplo:http://192.168.100.28:8585/frank/holabuendia
. Y antes del punto9
recuerden tener en escucha su puerto si es que van a ejecutar una reverse shell (si no, tienen que volver a hacer el commit).
Yo le pedí al hook
que me ejecutará una reverse shell:
Y LA TENGOOOOO! Me gustaaaaaaaaaaaaaaaaaa. Volvemos lindo esto y seguimos… ¿está como larga la máquina, no? kalsjdfklajsd
Escalada de privilegios #
Revisando los permisos que tiene frank
sobre otros usuarios notamos algo nuevo en nuestro arsenal (al menos en el mío):
Frank puede ejecutar (sin contraseña) como cualquier usuario (menos root
) el binario /usr/bin/sqlite3
, curioso, busquemos en internet si podemos explotar esto de alguna forma.
En GTFOBins (un repo con varias maneras de ejecutar cosas peligrosas mediante binarios de Unix) encontramos como obtener una Shell:
/usr/bin/sqlite3 /dev/null '.shell /bin/bash'
Ahora tendríamos que ver que usuarios interesantes existen en el sistema con los cuales queramos interactuar un rato :P
Entre la lista obtenida del /etc/passwd
hay uno muy juguetón, se llama syslog
. Este usuario tiene varios permisos asociados a logs y procesos internos del sistema, pero lo importante y fuerte es uno de los grupos que tiene asignados, el grupo adm
, con este grupo se puede llegar a muuuuchos más logs (alojados la mayoría en la ruta /var/log
) y trazas que va dejando el sistema, incluso podríamos llegar a ver comandos o intentos de autenticación sin cifrar, todo esto mediante logs
, así que exploremos.
sudo -u syslog /usr/bin/sqlite3 /dev/null '.shell /bin/bash'
Nos movemos a la ruta /var/log
y revisamos uno de los importantes, el objeto auth.log
:
💬 “
/var/log/auth.log
: All events related to authentication on Debian and Ubuntu server are recorded here. If you are looking for an event with the user authorization mechanism, you can find it in this log file.” ~ systemconf.com : critical linux log files.
Recorriendo el archivo vemos dos cosas llamativas, una donde se ve la creación del usuario frank
(esta tarea la ejecutó frank con sudo como el usuario root) en Gitea (esas credenciales no nos sirven para nada ya (ni reutilizándolas) :P):
Y que se ejecutó el mismo comando que ejecutamos para obtener una shell, pero esta vez frank lo hizo como el usuario #-1
:
¿Qué significará eso? A investigar…
Buscando en internet literalmente user #-1 linux
encontramos:
😨 😱 😨 😱 😨 😱 😨 😱 😨
¿Encontramos un bypass sin siquiera probarlo? Detallemos el exploit a ver si si:
Al parecer, esta explotación se encontró en las versiones menores a la 1.8.28
de sudo
(acá la info del CVE). Nosotros tenemooooos ‼️
frank@devguru:~$ sudo -V
Sudo version 1.8.21p2
...
Úpale, aplica, así que estemos atentooos~
La descripción de la vuln es sencilla:
❌
sudo
falla en la validación del user id (UID) del usuario que se agrega junto al-u
, al tratar de convertir estos valores a algo entendible, falla e interpreta el usuario#-1
como si su UID fuera el0
, que haria referencia al usuarioroot
💆
LOCOOOOO! Pues ya tiene sentido (aunque un poco feito que haya quedado eso como log, ya que entiendo que son pruebas que hizo el creador de la máquina para validar que sirviera todo (o quizás fue un atacante :P)), así que A pRoBar!
Ejecutamos el sudo
contra sqlite3
, peero le decimos que ahora sea como el usuario #-1
(que debería interpretarlo como si fuera root
) y nos otorgué una bash
:
ESOOOOOOOOOOOOOOOOOOOOOOOOO! Somos root
wowowowowowowowiiipiti!
Fiuuu, qué locura toda la máquina. Veamos las flags:
❓ Si te diste cuenta que estoy en una terminal SSH y no en la que obtuvimos como reverse shell? Es sencillito, a buscar! (que igual no es necesario, pero estamos comodos).
Liiiisto, hemos terminado la máquina (‘:
…
Muchas cosas se tocaron en esta resolución, me gusto resto el tema de reusar las contraseñas en adminer
, no sé, fue bien didáctico kajklakls.
En general, una máquina que recomiendo si o si, me gustó a bastante. Eso sí, diría que para ser media tiene demasiadas cosas, la pondría como hard para HackMyVM, pero si hablamos de HackTheBox, ahí si la pondría como media, de resto, fulero, me gustó.
Nos leemos después, muchos abrazos, éxitos y disciplina! A romper de todoooooo!
Comments