ApiBase
This content is not available in your language yet.
Introducción
Sección titulada «Introducción»En este artículo documentamos la resolución completa de la máquina virtual ApiBase, creada por El Pingüino de Mario y etiquetada como fácil en la plataforma DockerLabs. El objetivo es presentar el proceso de forma reproducible y didáctica: explicar qué se hizo, por qué y qué buscar en cada etapa.
El proceso se divide en tres fases principales:
- Reconocimiento – Identificar servicios activos, rutas accesibles, y posibles vectores de ataque.
- Explotación – Aprovechar vulnerabilidades detectadas para conseguir acceso inicial.
- Escalada de privilegios – Obtener control total del sistema (root) y documentar cómo y por qué fue posible.
Nota: los comandos y scripts se ejecutaron contra una instancia local/privada de DockerLab. No ejecutes pruebas intrusivas en redes o servicios para los que no tengas permiso explícito.
Información general
Sección titulada «Información general»- Nombre: ApiBase
- Autor: El Pingüino de Mario
- Dificultad: Fácil
- Fecha de creación: 27/02/2025
- Plataforma: DockerLabs
Paso 0 — Preparación, instalación y despliegue
Sección titulada «Paso 0 — Preparación, instalación y despliegue»-
Descargar la máquina desde el portal de DockerLabs.
-
Descomprimir el paquete:
Ventana de terminal unzip ApiBase.zip -
Desplegar la máquina (script de ejemplo proporcionado con la imagen):
Ventana de terminal sudo bash auto_deploy.sh ApiBase.tar
Ese script crea y arranca un contenedor Docker que expone los servicios necesarios. Tras ejecutar el script, la instancia debe estar accesible y lista para el análisis.
Paso 1 — Reconocimiento
Sección titulada «Paso 1 — Reconocimiento»1.1 Obtención de la IP
Sección titulada «1.1 Obtención de la IP»Al iniciar el contenedor, la IP asignada suele mostrarse en la salida del script de despliegue. Anótala y configúrate un directorio de trabajo para mantener todo ordenado:
mkdir -p ApiBase/{content,exploits,nmap,scripts}cd ApiBaseConsejo: usar un árbol de trabajo ayuda a reproducir los pasos y compartir hallazgos más tarde (por ejemplo, para escribir el writeup).

1.2 Escaneo de puertos
Sección titulada «1.2 Escaneo de puertos»Realizamos un port sweep completo con Nmap para detectar puertos TCP abiertos. Aquí usamos -Pn para evitar ping, -p- para todos los puertos, y un --min-rate alto para acelerar el escaneo en redes confiables:
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 172.17.0.2 -oG allPortsPosteriormente extraemos los puertos con un script auxiliar (o con grep):
# si tienes el script extractPortsextractPorts allPorts
Resultados relevantes: se detectaron los puertos 22 (SSH) y 5000 (servicio HTTP/REST). El puerto 5000 aloja una API web — un objetivo interesante para enumeración y explotación.
Paso 2 — Explotación
Sección titulada «Paso 2 — Explotación»Accedimos al servicio en http://172.17.0.2:5000. La API ofrecía, al menos, dos endpoints:
POST /add— Permite agregar usuarios.GET /users?username=<usuario>— Lista información del usuario porusername.

2.1 Exploración manual
Sección titulada «2.1 Exploración manual»Usamos una herramienta tipo cliente HTTP (por ejemplo, Bruno, Postman o curl) para inspeccionar behavior y respuestas:
- Probamos
POST /addcon varios cuerpos JSON hasta obtener la respuesta de user added. - Confirmamos que
GET /users?username=...devuelve información legible (HTML/JSON) si el usuario existe, y404si no.

Observación: si una API revela distintos códigos de estado según la existencia del recurso (200 vs 404), se puede abusar de ello para enumerar usuarios mediante fuerza bruta.
2.2 Automatización de enumeración de usuarios (fuerza bruta)
Sección titulada «2.2 Automatización de enumeración de usuarios (fuerza bruta)»Para automatizar la búsqueda de usuarios, desarrollamos un script en Python que realiza peticiones concurrentes al endpoint /users. A continuación incluyo una versión mejorada del script original:
- evita crear archivos adicionales,
- muestra únicamente intentos útiles (no-404),
- maneja timeouts, reintentos básicos y límite de concurrencia,
- muestra un resumen y tiempo de ejecución.
import http.clientfrom urllib.parse import quote_plusfrom concurrent.futures import ThreadPoolExecutor, as_completedimport timeimport argparse
HOST = "172.17.0.2"PORT = 5000ENDPOINT = "/users"BODY_PREVIEW = 200 # caracteres máximos mostrados del body
def check_user(host, port, endpoint, username, timeout=8, retries=1): """Consulta /users?username=<username>. Devuelve (username, status, body)""" username_q = quote_plus(username) path = f"{endpoint}?username={username_q}" for attempt in range(retries + 1): conn = http.client.HTTPConnection(f"{host}:{port}", timeout=timeout) try: conn.request("GET", path) res = conn.getresponse() body = res.read().decode("utf-8", errors="replace") return username, res.status, body except Exception as e: last_exc = e # reintentar si quedan intentos finally: try: conn.close() except: pass return username, None, f"Error: {last_exc}"
def main(args): # cargar wordlist with open(args.input_file, "r", encoding=args.encoding, errors="ignore") as f: users = [line.strip() for line in f if line.strip()]
print(f"[*] Cargando {len(users)} usuarios desde {args.input_file}...") total = 0 useful = 0 start = time.time()
with ThreadPoolExecutor(max_workers=args.max_workers) as executor: futures = {executor.submit(check_user, HOST, PORT, ENDPOINT, u, args.timeout, args.retries): u for u in users}
for future in as_completed(futures): total += 1 username, status, body = future.result()
# sólo mostramos respuestas útiles (no-404 y status presente) if status is not None and status != 404: useful += 1 preview = body[:BODY_PREVIEW] + ("..." if len(body) > BODY_PREVIEW else "") print(f"[ÚTIL] {username} -> {status}") print(preview) print("-" * 60)
# avance simple por consola if total % 100 == 0: elapsed = time.time() - start print(f"[+] Progresso: {total}/{len(users)} (tiempo: {elapsed:.1f}s)")
elapsed = time.time() - start print(f"\nResumen: probados = {total}, útiles (no-404) = {useful}, tiempo = {elapsed:.2f}s")
if __name__ == "__main__": p = argparse.ArgumentParser(description="Enumera usuarios via /users?username= on ApiBase") p.add_argument("--input-file", default="/usr/share/seclists/Usernames/xato-net-10-million-usernames-dup.txt") p.add_argument("--max-workers", type=int, default=20) p.add_argument("--timeout", type=int, default=8) p.add_argument("--retries", type=int, default=1) p.add_argument("--encoding", default="latin-1") args = p.parse_args() main(args)Puntos a destacar del script mejorado:
argparsepermite ajustar la wordlist, concurrencia y timeouts sin editar el código.- Se ignoraron las respuestas
404y sólo se muestran los resultados útiles, tal como pediste. - Implementa reintentos simples y un progreso básico para no perder visibilidad del avance.
- No genera archivos temporales: todo sale por consola.
Ejecutamos el script y obtuvimos varios usuarios válidos (capturas):

2.3 Ingreso por SSH con credenciales enumeradas
Sección titulada «2.3 Ingreso por SSH con credenciales enumeradas»Con al menos un usuario válido (Pingu) identificado, intentamos autenticación por SSH. Si la máquina usa credenciales débiles o contraseñas recolectadas (en este CTF había un conjunto de credenciales válidas), se puede obtener acceso:

Con esto conseguimos una sesión de usuario no privilegiado (Pingu).
Nota: en entornos reales, usar técnicas de fuerza bruta y acceso sin autorización es ilegal. En CTFs y laboratorios, está permitido siempre que la plataforma lo autorice.
Paso 3 — Escalada de privilegios
Sección titulada «Paso 3 — Escalada de privilegios»Una vez dentro como Pingu, realizamos un listado y enumeración básica del sistema:
sudo -lno estaba disponible (o no daba permisos).- No se encontraron binarios con bit SUID evidentes.
- Hicimos búsqueda de archivos interesantes: scripts, copias de seguridad, y capturas de red.
Se identificó un archivo .pcap (captura de tráfico) accesible por el usuario. La apertura del .pcap con Wireshark/tshark reveló credenciales en texto plano dentro de la captura.


En la captura apareció una contraseña usable para la cuenta root. Con esa contraseña pudimos iniciar sesión como root:

Conclusión y aprendizajes
Sección titulada «Conclusión y aprendizajes»Resumen de la cadena de explotación:
- Servicio HTTP en puerto
5000con endpoints que diferenciaban respuestas entre usuario existente y no existente. - Enumeración de usuarios mediante fuerza bruta (script concurrente) hasta encontrar uno válido (
Pingu). - Acceso por SSH con credenciales obtenidas o probadas.
- Enumeración local del sistema que reveló una captura de red (
.pcap) conteniendo credenciales en texto plano. - Autenticación como
rootcon la contraseña encontrada en la captura.