Dificultad | Very Easy |
|---|---|
Plataforma | Hack The Box (Starting Point - Tier 1) |
SO objetivo | Linux |
Servicios | HTTP (puerto 80) |
Técnicas | SQL Injection (bypass de autenticación) |
Flag | Flag única (no requiere escalada) |
Tiempo estimado | 10 minutos |
Introducción
Appointment es una máquina pensada para dar el primer contacto con una de las vulnerabilidades web más clásicas y a la vez más peligrosas: la inyección SQL (SQL Injection, o SQLi). No hay escalada de privilegios ni movimiento lateral: todo el reto consiste en identificar un formulario de login vulnerable y usarlo para saltarnos la autenticación sin conocer ninguna credencial válida.
Es un ejercicio perfecto para entender, de forma práctica, por qué nunca hay que confiar en la entrada del usuario tal cual llega al servidor.
Reconocimiento
Como en cualquier evaluación, el primer paso es entender qué servicios expone la máquina. Para esto se usa nmap, la herramienta estándar de escaneo de puertos y detección de servicios:
nmap -p- -sC -sV -O -T4 10.129.155.69 --min-rate 5000
Explicación rápida de las flag usadas:
-p-: escanea los 65535 puertos, no solo los más comunes.
-sC: ejecuta scripts básicos de reconocimiento (nmap scripting engine) para obtener info adicional de cada servicio.
-sV: intenta detectar la versión exacta del software que corre en cada puerto abierto.
-O: intenta identificar el sistema operativo del host.
-T4: ajusta la velocidad del escaneo a "agresiva" para que tarde menos.
--min-rate 5000: fuerza a nmap a enviar al menos 5000 paquetes por segundo, acelerando aún más el escaneo.
Resultado:
Solo hay un puerto abierto: el 80/tcp, corriendo un servidor web Apache 2.4.38 sobre Debian. El detalle más interesante ya está ahí mismo, en el título de la página: Login. Esto nos dice de entrada que toda la superficie de ataque de esta máquina va a estar concentrada en una aplicación web con un formulario de autenticación.
Enumeración
Con un solo servicio expuesto (HTTP), el siguiente paso lógico es explorar qué hay dentro del sitio web más allá de la página principal. Para esto se usa gobuster, una herramienta de fuerza bruta de directorios y archivos: le damos un diccionario de nombres comunes y va probando cuáles existen en el servidor a partir de los códigos de respuesta HTTP.
gobuster dir -u http://10.129.158.228 -w /usr/share/wordlists/dirb/common.txt -x php,txt,html
dir: modo de enumeración de directorios/archivos.
-u: la URL objetivo.
-w: el diccionario (wordlist) a usar, en este caso uno estándar de Kali Linux.
-x php,txt,html: le indica a gobuster que además pruebe cada palabra del diccionario con esas extensiones (por ejemplo, no solo admin, sino también admin.php).
Nota: la IP cambia respecto al escaneo de nmap porque las máquinas de HTB Starting Point se reinician con una IP nueva cada vez que se despliegan; sigue siendo la misma máquina Appointment.
Resultado relevante:
index.php (Status: 200) [Size: 4896]
css/ (Status: 301)
js/ (Status: 301)
fonts/ (Status: 301)
images/ (Status: 301)
vendor/ (Status: 301)
No aparece nada llamativo tipo panel de administración oculto o archivo de configuración expuesto: básicamente solo se confirma la estructura típica de un sitio armado con un framework/plantilla web (carpetas de css, js, fonts, vendor). El index.php con código 200 es la página principal, la misma que ya habíamos visto anunciada como Login en el nmap.
Como no hay más rutas interesantes que explorar, el foco pasa directamente al formulario de login que ya identificamos: si toda la aplicación gira alrededor de esa única funcionalidad, es ahí donde hay que buscar la vulnerabilidad.
Explotación (obtención de acceso)
Al entrar a http://<IP>/, se encuentra el formulario de autenticación:
Causa raíz del problema: muchas aplicaciones mal programadas arman sus consultas a la base de datos concatenando directamente lo que el usuario escribe en el formulario, sin sanitizar ni usar consultas parametrizadas. Cuando eso pasa, el backend probablemente construye algo parecido a esto internamente:
SELECT * FROM users WHERE username = 'INPUT_USUARIO' AND password = 'INPUT_PASSWORD';
Si el valor que ponemos en el campo de usuario se inserta tal cual dentro de las comillas, podemos "escapar" de la lógica original de la consulta usando caracteres especiales de SQL.
El payload usado:
- Usuario: admin'#
- Contraseña: a
¿Por qué funciona esto?
La comilla simple (') cierra anticipadamente la cadena de texto donde se supone que iba el nombre de usuario. A partir de ahí, lo que sigue ya no se interpreta como texto, sino como parte de la sentencia SQL.
El símbolo # es un operador de comentario en MySQL/MariaDB: todo lo que venga después de él, en esa misma línea, la base de datos lo ignora por completo.
Entonces la consulta que realmente termina ejecutando el servidor queda así:
SELECT * FROM users WHERE username = 'admin'#' AND password = 'a';
Pero como el # comenta el resto de la línea, para la base de datos la consulta real es simplemente:
SELECT * FROM users WHERE username = 'admin'
Este es el punto clave: la verificación de la contraseña desaparece por completo de la ecuación. La base de datos ya no le pide al usuario que la contraseña coincida con nada, porque esa parte de la condición fue comentada antes de llegar a evaluarse. Por eso el valor que se ponga en el campo de contraseña es literalmente irrelevante; puede ser una letra a, un espacio, o cualquier otra cosa: la aplicación va a autenticar como admin de todas formas.
Al enviar el formulario con esos valores, la aplicación devuelve la flag directamente:
Congratulations!
Your flag is: e3d0796d002a446c0e622226f42e9672
Flag
Obtenida directamente tras el bypass de login mediante SQL injection. Al ser una máquina de un solo objetivo, no hay fases separadas de user flag y root flag.
Lecciones aprendidas / Conclusión
Appointment es un ejemplo muy directo de por qué la inyección SQL sigue estando en el top de las vulnerabilidades web más críticas (forma parte constante del OWASP Top 10) a pesar de ser un problema conocido desde hace más de 20 años.
Puntos clave:
Un formulario de login "normal" puede ser el punto de entrada completo a un sistema si la lógica detrás no valida correctamente lo que el usuario envía.
Caracteres aparentemente inofensivos como ' y # pueden alterar por completo el comportamiento de una consulta a base de datos si no se manejan bien.
Un gobuster sin hallazgos "jugosos" no significa que no haya vulnerabilidad: a veces la superficie de ataque más simple (un único formulario) es justamente la vulnerable.
Desde la perspectiva defensiva, esto se previene con:
Consultas parametrizadas / prepared statements, en vez de concatenar strings directamente en el SQL. Esto hace que el motor de base de datos trate cualquier entrada del usuario siempre como dato, nunca como código.
Validación y sanitización de entradas en el backend, nunca confiando solo en validaciones del lado del cliente (JavaScript).
Uso de un ORM (Object-Relational Mapping) bien configurado, que por diseño evita construir SQL manualmente.
Principio de mínimo privilegio en la cuenta de base de datos que usa la aplicación, para limitar el daño si igual llega a explotarse una inyección.