Datos del reto
| Campo | Valor |
|---|---|
| CTF | Nullcon CTF 2026 |
| Reto | Web2Doc2 |
| Categoría | Web |
| Flag | ENO{weasy_pr1nt_can_h4v3_f1l3s_1n_PDF_att4chments!} |
Descripción del reto
El servicio es el mismo que en Web2Doc v1 (convertidor URL → PDF en Flask), pero sin el endpoint /admin/flag. El objetivo sigue siendo leer el contenido de /flag.txt en el servidor.
- El backend usa WeasyPrint para generar el PDF (metadata:
Producer: WeasyPrint 68.1). - No se puede pasar
file:///flag.txtcomo URL directamente: el backend hace fetch de la URL (p. ej. conrequests), que falla o bloqueafile://. - La idea: el servidor sí cargue una URL pública que devuelve HTML; ese HTML puede contener referencias a recursos que WeasyPrint resuelve en el servidor. Ahí entra el abuso de adjuntos PDF.
Reconocimiento
En HTML, WeasyPrint soporta adjuntos al PDF mediante:
<link rel="attachment" href="URL" title="descripción">
Si href es una URL que el url_fetcher de WeasyPrint puede resolver, el contenido se incrusta como archivo adjunto. Al procesar HTML obtenido desde una URL pública, el motor puede seguir resolviendo file:// para esos adjuntos en el contexto del servidor, por lo que puede leerse /flag.txt y terminar embebido en el PDF.
Análisis de la vulnerabilidad
Vector: HTML público con <link rel="attachment" href="file:///flag.txt">. WeasyPrint resuelve file:// para adjuntos en el contexto del servidor; el contenido de /flag.txt queda embebido en el PDF y se puede extraer del stream.
Explotación
-
HTML malicioso: Crear una página con:
<link rel="attachment" href="file:///flag.txt" title="flag"> -
Alojar el HTML en una URL pública (Litterbox, Gist + raw.githack, etc.).
-
Convertir a PDF en el reto: En la web del reto, resolver el captcha y enviar en
/convertla URL del paso 2. El servidor descarga el HTML, se lo pasa a WeasyPrint; WeasyPrint genera el PDF y, al procesar el adjunto, lee/flag.txten el servidor y lo incluye como adjunto en el PDF. -
Extraer la flag del PDF: Descargar el PDF, localizar el stream del adjunto (FlateDecode) y extraer la cadena
ENO{...}.
Automatización: El script solve.py genera el HTML, lo sube a Litterbox, obtiene el captcha, llama a /convert, lee el PDF y extrae la flag. Ejecución: pip install requests && python3 solve.py.
Flag
ENO{weasy_pr1nt_can_h4v3_f1l3s_1n_PDF_att4chments!}
Lecciones aprendidas
| Concepto | Detalle |
|---|---|
| Objetivo | Leer /flag.txt en el servidor (sin endpoint /admin/flag). |
| Vector | HTML público con <link rel="attachment" href="file:///flag.txt">. |
| Motor | WeasyPrint (PDF); el backend obtiene el HTML por HTTP. |
| Abuso | WeasyPrint resuelve file:// para adjuntos en el contexto del servidor. |
| Exfiltración | Contenido de /flag.txt embebido en el PDF; se extrae del stream. |