altered

Reconocimiento inicial

Como siempre comenzamos con un scan para detectar los puertos abiertos en el host victima:

nmap -sS --min-rate 1500 -p- --open -n -Pn 10.10.10.85

nmap1

Como se aprecia solo se encuentra el puerto 3000 abierto. Realzamos otro scan para obtener los servicios y versiones que se están utilizando en ese puerto:

nmap -sCV -p3000 10.10.10.85

nmap2

Y observamos que corresponde a un servicio web en el cual se esta utilizando Node.js Express framework. Con esto presente ingresamos a la web para visualizar el contenido de esta.

Reconocimiento web

Vemos que al ingresar se muestra el mensaje Hey Dummy 2+2 is 22: web1 Luego de enumerar un poco notamos que la web no tiene nada más que esto. Observando las cookies de sesión encontramos un valor parecido a un JWT pero en realidad es un valor en base64 ya que no contiene los puntos de la firma de un JWT normal:
fakejwt Al decodificar el valor en base64 notamos que es un objeto que esta serializado:
modifiedparam Si tomamos estos valores y los modificamos como por ejemplo el valor de username y lo enviamos en la cookie de sesión podemos ver que efectivamente cambian:
changeok changed

Node JS Deserialization Attack

Buscando damos con el siguiente post:
Exploiting Node.js deserialization bug for Remote Code Execution

El cual explica un método en el cual es posible realizar una ejecución remota de comandos mediante la explotación de deserialización insegura en node.js. En el cual se nos explica que:

Los datos no confiables pasados a la función unserialize() en el módulo node-serialize pueden ser explotados para lograr la ejecución arbitraria de código pasando un objeto JavaScript serializado con una expresión de función invocada inmediatamente (IIFE) Immediately invoked function expression.

→ Construcción del Payload

Para construir el payload es necesario contar con la librería de node.js node-serialize. Está la podemos instalar con npm:
nodeserialize Al ejecutarlo observamos que nos crea el objeto:
obcreated

Como menciona el post, Ahora tenemos una cadena serializada que puede ser deserializada con la función unserialize(). Pero el problema es que la ejecución del código no ocurrirá hasta que disparemos la función correspondiente a la propiedad rce del objeto.

Más tarde descubrí que podemos usar la expresión de función invocada inmediatamente (IIFE) de JavaScript para llamar a la función. Si usamos el doble paréntesis de IIFE () después del cuerpo de la función, la función será invocada cuando el objeto sea creado. Funciona de forma similar a un constructor de clase en C++.

Ahora se llama a la función serialize() con el código del objeto modificado:
iife La vulnerabilidad en la aplicación web es que lee la cookie de la petición HTTP, realiza la decodificación base64 del valor de la cookie y lo pasa a la función unserialize(). Como la cookie es una entrada no confiable, un atacante puede crear un valor de cookie malicioso para explotar esta vulnerabilidad.

Shell as Sun

Para agilizar la explotación podemos utilizar la herramienta nodejsshell.py para obtener una reverse shell:
shellcommand Copiamos el payload entregado y lo reemplazamos en el primer script de la siguiente forma:
shellreplaced Desde ya nos ponemos en escucha:
nclisten Y codificamos el payload en base64 para enviarlo en la cookie:
codedpayload cookiereplace Observamos que ya tenemos acceso:
shellrec

Privesc

Al recibir la conexión notamos que estamos en el directorio del usuario sun. Dentro de este notamos el fichero output.txt:
sunfolder El cual si observamos podemos notar el mensaje de: Script is running... Pero no sabemos que script. Además notamos que el output es generado por el usuario root:
rootfile

Detectando procesos con Pspy

En base al fichero detectado podemos pensar que se esta ejecutando un script que no podemos detectar. Para esto utilizaremos la herramienta pspy la cual debemos transferir a la maquina victima:
psytransfered Le damos permisos de ejecución y la ejecutamos. Y luego de esperar por un rato podemos observar que el UID 0 correspondiente al usuario root, está ejecutando el siguiente comando:
pspydetect Vemos que root ejecuta el script script.py en el directorio Documents del usuario sun para luego depositar el fichero output.txt. Al observar el contenido del script podemos ver que simplemente ejecuta un print de “Script is running…” por lo que realmente no hace nada. Pero lo curioso de esto es que el propietario del script es el usuario que ya tenemos comprometido:
script En base a esto podemos modificar el script para que se cambie el permiso de la /bin/bash a SUID:
change Ahora debemos esperar a que el usuario root ejecute el script para poder cambiar el permiso de la /bin/bash y escalar privilegios al usuario root:
suid

Shell as root

Ya con permiso SUID en la bash podemos utilizar el parámetro -p para ejecutarla con permisos de root:
pwned Con esto ya podemos leer la flag del usuario root.

Pwned! 🏴‍☠️