Skip to main content

Web2Doc2 – Nullcon CTF 2026

nullcon CTF 2026

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.txt como URL directamente: el backend hace fetch de la URL (p. ej. con requests), que falla o bloquea file://.
  • La idea: el servidor 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

  1. HTML malicioso: Crear una página con:

    <link rel="attachment" href="file:///flag.txt" title="flag">
    
  2. Alojar el HTML en una URL pública (Litterbox, Gist + raw.githack, etc.).

  3. Convertir a PDF en el reto: En la web del reto, resolver el captcha y enviar en /convert la 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.txt en el servidor y lo incluye como adjunto en el PDF.

  4. 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.