- Dificultad:
easy - Tiempo aprox.
6h - Datos Iniciales:
10.10.11.122
Nmap Scan#
Tras realizar un escaneo nmap completo, se encuentran los siguientes puertos abiertos:
| |
Varios puertos abiertos:
- 22/TCP (SSH): Versión no vulnerable, no podemos hacer mucho de momento.
- 80/TCP (HTTP): Un servidor web, nuestro primer sitio a mirar.
- 443/TCP (HTTPS): Otro (o el mismo) servidor web, con https.
- 5353/UDP (ZeroConf/mDNS)
- Zeroconf es un conjunto de tecnologías diseñadas para crear redes IP automáticamente, sin necesidad de configuración manual ni servidores centrales (DHCP o DNS)
- La máquina está usando mDNS (puerto 5353), es el protocolo subyacente más común para implementar Zeroconf.
- mDNS permite que los dispositivos en una red local se descubran entre sí y resuelvan hostnames sin un servidor DNS dedicado.
Ignoramos SSH de momento, y, dado que como se indica en HackTricks es posible enumerar ciertas cosas de la máquina por mDNS, vamos a por ello antes de lanzarnos a HTTP(S).
ZeroConf, mDNS#
Empiezo probando varios scripts de nmap, pero ninguno parece dar resultado
| |
| |
Con dig tampoco obtenemos nada:
| |
No conseguimos enumerar nada de mDNS, así que vamos a por el servicio web.
HTTP(S)#
Al conectarnos a HTTP se nos redirige a HTTPS, así que ambos puertos sirven lo mismo.
| |
Al entrar encontramos una página de venta online. Permite crear cuentas, así que tratamos de crear una:

No es posible registrarse, tampoco conocemos ningún email y contraseña para iniciar sesión. Tenemos que seguir enumerando.
| |
No encontramos mucho en ninguno de los directorios. Probamos a enumerar vhosts:
| |
Encontramos store.nunchucks.htb, lo añadimos a /etc/hosts.
Subdominio store y posible SSTI#
Al entrar, nos encontramos con la siguiente página:

Vemos que cuando introducimos un email, aparece un texto debajo indicando el email al que serán mandada la info, es decir, nuestro input.

Esto nos indica que puede estar tratándose de un caso de SSTI, así que probamos con varios inputs, como ${7*7}:

La primera prueba no funciona, probamos con {{ 7*'7' }}:

Y esta sí funciona.
Dado que sabemos que {{ 7*'7' }} devuelve 49 y que whatweb indica que la página usa ExpressJS
| |
podemos buscar un Template engine que funcione con ExpressJS.
Tras una búsqueda, encontramos Nunjucks, cuyo nombre es sospechosamente parecido al de la máquina.
Nunjucks y RCE - Documentación & Explicación#
En la documentación de Nunjucks encontramos lo siguiente:
nunjucks does not sandbox execution so it is not safe to run user-defined templates or inject user-defined content into template definitions. On the server, you can expose attack vectors for accessing sensitive data and remote code execution…
En esta página encontramos info acerca de la posibilidad de conseguir RCE haciendo uso de un payload como el siguiente:
| |
Explicación:
- Nunjucks provee
rangecomo una función por defecto. Usarrange.constructorpermite acceder al constructor de funciones de Javascript.
rangeno tiene nada de especial, es simplemente un objeto disponible en este contexto. Se usa porque en jsx.constructorlleva a la clase que creóx. ComoFunctioncreórange,range.constructorlleva aFunction
- En js, el constructor
Functiontoma strings como argumentos y los convierte en el cuerpo de una función nueva.
P.ej y si tenemos en cuenta que en este caso range.constructor equivale a Function, range.constructor("return x") sería equivalente a:
| |
Y añadir un “()” al final llamaría directamente a la función creada, es decir, range.constructor("return x")() sería equivalente a:
| |
Esto significa que nuestro payload hace algo como:
| |
Al ejecutar la función, hace lo siguiente:
- Salta al entorno global (
global) y dentro de él al objeto del proceso actual (process) - Entra a
MainModule, que representa el script principal que inició la aplicación y que en general debería tener acceso a funciones para importar librerías. - Importa el módulo
child_process - Llama al método
execSynccon el comando como parámetro (whoami) - Convierte el output a string y lo devuelve
Nunjucks y RCE - Explotación#
Probamos a ejecutar algunos comandos con nuestro payload:
whoami:
| |
Devuelve:
| |
Vemos que se ejecuta como usuario david.
Tras probar a ejecutar varios reverse shells, ninguno funcionaba (ni encodeando en base64). Decido probar a crear un par de claves ssh para david y copiar la privada:
| |
La clave privada (mykey2) habrá que copiarla a la máquina atacante, la clave pública (mykey2.pub) habrá que copiarla a authorized_keys dentro de /home/david/.ssh/
Mostramos en el navegador la clave privada:
| |
y en mi caso la guardo a id_ed25519. Además, cambio cada \n por un salto de línea literal (dado que el comando ha devuelto un string con \n literales en lugar de saltos de línea)
Luego copio mykey2.pub a authorized_keys:
| |
Y por último intento conectarme:
| |
Escalada de privilegios#
Al ejecutar linpeas.sh destacan varias cosas:
MUY IMPORTANTE: Files with capabilities: /usr/bin/perl = cap_setuid+epMUY IMPORTANTE: Según linpeas, vulnerable a CVE-2021-3560IMPORTANTE: Binariopkexeccon SUID bitMEDIO: Archivo /etc/apparmor/severity.db: ASCII text
Nota: Capabilities y root
Normalmente, en Linux hay 2 tipos de usuarios (ignorando los de servicio): usuarios normales, y root. Antiguamente, si un programa quería hacer algo especial, había que dar permiso SUID al binario, lo que resultaba peligroso porque, quizás para hacer una cosa sencilla, se le otorga al binario el poder de administrador total.La intención de las capabilities es dividir el poder de root en partes pequeñas específicas, como abrir puertos bajos (CAP_NET_BIND_SERVICE) o cambiar la hora del sistema (CAP_SYS_TIME).
Las capabilities están hechas para reducir el peligro que supondría otorgar root, pero el peligro sigue presente si la capability es crítica, como en este caso, con CAP_SETUID+EP.
cap_setuid+eppermite al proceso cambiar su propio UID de forma arbitraria, es decir, puede, cuando quiera, convertirse a root (UID 0).
Para ello, ejecutamos un script de perl que cambie el UID del proceso a root (0) y ejecute un shell:
| |
| |
| |
Y tenemos root.




