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
Última actualización
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
Última actualización
Empezamos siendo organizados:
Creamos el directorio para la máquina y lanzamos un bonito mkt para crear el entorno de trabajo.
Nos apuntamos la IP en nuestro .txt y lanzamos un escáner SYN general con NMAP para enumerar los posibles puertos abiertos:
Recuerda estudiar la teoría:
El modo de scan SYN de NMAP ( -sS) necesita de permisos de super-user ya que durante este escáner se modifica el comportamiento por defecto del protocolo 3 way handshake. Durante este escáner silencioso, no se llega a enviar la última flag ACK sino que mandamos una RST, de manera que nunca se completa la conexión por TCP.
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:
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.
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.
Vayamos al navegador para comprobar qué tenemos en el servicio HTTP.
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?
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.
Esta es la misma petición GET que teníamos en el navegador pero enviada en Postman
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
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
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.
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.
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
Mediante este método recibimos una respuesta diferente:
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>
Para realizar el siguiente fuzzing vamos a utilizar la herramienta ffuf con la siguiente sintaxis:
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.
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.
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
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
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:
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.
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.
Lo primero de todo tras ganar acceso, tratamiento de TTY para nuestra comodidad:
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
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
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
No funcionaba.
Probé con el mismo servidor + curl
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.
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)
Una vez transferido el directorio, vamos a utilizar la herramienta de firefox_decrypt para extraer la contraseña del perfil de Firefox.
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.
Te enseño a hacerlo:
Ahora ya podemos usar la herramienta cómodamente:
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
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.
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
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.
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
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.
Enumerar detenidamente el código fuente
Probar diferentes métodos HTTP en una misma petición
Fuzzing de parámetros
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.