AVILX
HACKER101 CTF · WEB · 2026
Micro CMS v1
XSS almacenado, IDOR y SQL Injection en un CMS minimalista
| Plataforma | Hacker101 CTF |
| Categoría | Web |
| Dificultad | Easy |
| Flags | 4 / 4 |
| Analista | Jesús Ávila (Avilx) |
| Fecha | 07 de Septiembre 2026 |
| Portafolio | avilx.dev |
Documentación de aprendizaje ofensivo | Entorno de laboratorio autorizado
Contexto
Descripción del objetivo
Micro CMS v1 es un gestor de contenido minimalista expuesto en Hacker101 CTF como reto de nivel Easy. La aplicación permite crear, editar y visualizar páginas con soporte de Markdown y HTML. El objetivo es encontrar 4 flags ocultas explotando vulnerabilidades en la lógica de la aplicación.
Vulnerabilidades presentes
-
Stored XSS en el campo body mediante event handler
onerror— bypass del filtro de<script> -
Stored XSS en el campo title renderizado sin sanitizar en la página home
-
IDOR (Insecure Direct Object Reference) en el endpoint
/page/edit/IDsin control de acceso -
SQL Injection en el parámetro ID del endpoint
/page/edit/
Las flags en Hacker101 tienen el formato
^FLAG^...hex...$FLAG$. No aparecen como popup externo — están embebidas en el HTML de la página cuando el exploit es correcto. Siempre revisar el source con Ctrl+U.Reconocimiento de la Aplicación
Superficie de ataque inicial
Al acceder a la aplicación se identificaron las siguientes rutas y funcionalidades disponibles:
-
/— Home con listado de páginas existentes -
/page/1— Página “Testing” -
/page/2— Página “Markdown Test” -
/page/edit/1— Formulario de edición con campos Title y Body -
/page/create— Creación de páginas nuevas
Hallazgos del reconocimiento
La aplicación indica explícitamente “Markdown is supported, but scripts are not”. Al revisar el source del body de “Markdown Test” se encontró la etiqueta
<button>Some button</button> renderizándose como HTML real — la app no solo procesa Markdown sino HTML directo en el body. Además, las páginas usan IDs numéricos secuenciales (/page/1, /page/2), lo que sugiere un vector de enumeración.
Vectores identificados
Del reconocimiento inicial se trazaron tres líneas de ataque:
-
XSS en los campos Title y Body — el filtro es parcial
-
IDOR — IDs numéricos secuenciales en la URL
-
SQLi — parámetros numéricos que posiblemente alimentan queries SQL directamente
Flag 0 — IDOR en endpoint de edición
Identificación del vector
Las páginas usan IDs numéricos secuenciales. Si existen páginas privadas con IDs que no aparecen en el listado público, podrían ser accesibles directamente manipulando el parámetro en la URL.
Se enumeró manualmente incrementando el ID en la URL. Al acceder a /page/6 la app retornó 403 Forbidden — la página existe pero su vista está protegida. Sin embargo, el control de acceso no estaba implementado en el endpoint de edición.
Explotación
Request:
GET /page/edit/6 HTTP/1.1
Host: [lab-id].ctf.hacker101.com
El endpoint
/page/edit/6 no verifica si el usuario tiene permisos para editar esa página. La app implementa restricción en /page/ID pero olvidó aplicar la misma lógica en /page/edit/ID, exponiendo el contenido completo de páginas marcadas como privadas.Payload:
GET /page/edit/6Flag 1 — Stored XSS en body con bypass de filtro
Identificación del vector
La app dice que scripts no están soportados pero renderiza HTML. Un filtro que solo bloquea
<script> puede bypassearse con event handlers que ejecuten JavaScript sin usar esa etiqueta.Se probó
<script>alert(1)</script> en el body. La app lo filtró y solo mostró el texto alert(1) — el filtro apunta específicamente a la etiqueta <script>.Explotación
Se utilizó un event handler onerror en una etiqueta de imagen con src inválido para forzar la ejecución de JavaScript:
Payload:
<img src=x onerror=alert(document.cookie)>
La flag no apareció en el popup sino en el source de la página:
El filtro bloquea
<script> pero no sanitiza event handlers en otras etiquetas HTML. Hacker101 inyectó la flag como atributo flag en el elemento img generado. La flag no era visible en la página renderizada — solo en el source con Ctrl+U.Flag 2 — Stored XSS en title renderizado en home
Identificación del vector
El campo Title escapa HTML en
/page/ID pero si el home lista los títulos sin sanitizar, el XSS se ejecutaría allí en lugar del punto de inserción.Se insertó
<img src=x onerror=alert(1)> en el Title. Al visitar /page/ID el payload se mostró como texto plano — parecía que el campo no era vulnerable. El vector real no estaba donde se inserta el dato sino donde se consume.Explotación
Se guardó el payload en el campo Title y al regresar al home el alert se ejecutó automáticamente:
Payload:
<img src=x onerror=alert(1)> en el campo Title
El campo Title sanitiza correctamente en
/page/edit/ID y en /page/ID, pero el home renderiza los títulos sin escapar el HTML. Un payload en el Title se ejecuta cada vez que cualquier usuario visita el home — Stored XSS con mayor impacto que el del body porque afecta a todos los visitantes.Flag 3 — SQL Injection en parámetro ID
Identificación del vector
Los IDs numéricos en
/page/edit/ID posiblemente alimentan una query SQL sin sanitizar. Una comilla simple podría romper la sintaxis y generar un error visible.Se probó
/page/1’ — retornó 404 genérico porque ese endpoint valida que el ID sea numérico antes de la consulta. El endpoint /page/edit/ tenía una lógica diferente.
Explotación
Request:
GET /page/edit/1' HTTP/1.1
Host: [lab-id].ctf.hacker101.com
El endpoint
/page/edit/ no valida ni sanitiza el parámetro ID antes de usarlo en la query SQL. La comilla simple rompió la sintaxis y la app expuso la flag directamente en la respuesta. A diferencia de /page/1’ que retornaba 404, el endpoint de edición pasa el input directo a la base de datos.Payload:
/page/edit/1’Impacto
Resumen de severidad
| Vulnerabilidad | Confidenc. | Integridad | Disponib. | Severidad |
|---|---|---|---|---|
| IDOR | Alto | Alto | Ninguno | HIGH |
| Stored XSS (body) | Alto | Medio | Ninguno | HIGH |
| Stored XSS (title) | Alto | Alto | Ninguno | CRITICAL |
| SQL Injection | Alto | Alto | Alto | CRITICAL |
Impacto técnico por vulnerabilidad
Remediación
-
IDOR: verificar en todos los endpoints que el usuario tiene permisos sobre el recurso — no solo en la vista de lectura
-
XSS: sanitizar el output en cada punto donde se renderiza input del usuario, no solo donde se ingresa
-
SQLi: usar prepared statements. Nunca concatenar input del usuario directamente en una query SQL
Reflexión Final
Qué salió bien
-
El reconocimiento inicial fue completo — se mapearon todas las rutas y funcionalidades antes de intentar cualquier exploit
-
La detección del XSS en el body fue rápida al observar que la app renderizaba HTML y el filtro era parcial
-
El IDOR se identificó por enumeración sistemática de IDs numéricos secuenciales
Qué salió mal
-
No se entendía cómo Hacker101 registra las flags — se asumió sistema externo cuando la flag está embebida en el HTML. Siempre revisar el source con
Ctrl+U -
El XSS en el Title se descartó prematuramente al no ejecutar en
/page/ID— no se consideró que podía ejecutarse en una página diferente (el home)
Lecciones aprendidas
-
Un filtro de
<script>no es protección contra XSS: existen decenas de vectores alternativos — event handlers (onerror,onload,onclick), SVG, atributoshrefconjavascript:. Bloquear una etiqueta específica es seguridad por oscuridad, no sanitización real -
El punto de ejecución del XSS no siempre es donde se inserta el payload: un Stored XSS puede ejecutarse en una página completamente diferente que consume ese dato sin sanitizar. Hay que rastrear el flujo del dato desde la inserción hasta todos los puntos donde se renderiza
-
IDOR requiere verificar todos los endpoints por separado: la app protegía
/page/6con 403 pero olvidó aplicar el mismo control en/page/edit/6. Cada endpoint es una superficie de autorización independiente -
En Hacker101 la flag está en el HTML: la plataforma inyecta la flag directamente en el HTML cuando el exploit es correcto. Revisar siempre el source después de cada payload
Resumen de flags
Flag 0 IDOR /page/edit/6 sin control de acceso
Flag 1 Stored XSS <img onerror> en body
Flag 2 Stored XSS <img onerror> en title, ejecuta en home
Flag 3 SQL Injection comilla en /page/edit/1’
Tiempo total
Aproximadamente 1 Hora y 35 Minutos incluyendo investigación del mecanismo de flags de Hacker101 y investigacion de las vulnerabilidades.