Use the tree to jump between collections without leaving the reader.

archive Select writeup Open tree
NullconCTF2026/WriteupWeb2Doc2.en.md READ_ONLY

Web2Doc2

Challenge Data

Field Value
CTF Nullcon CTF 2026
Challenge Web2Doc2
Category Web
Flag ENO{weasy_pr1nt_can_h4v3_f1l3s_1n_PDF_att4chments!}

Challenge Description

The service converted a URL into a PDF using Flask and WeasyPrint. Unlike the previous version, there was no convenient endpoint for reading the flag anymore, so the PDF generator itself had to be used to read /flag.txt.


Reconnaissance

The first obvious idea was to submit file:///flag.txt as the main URL, but that did not work because the backend downloaded the page before handing it to WeasyPrint. Looking a bit more carefully at how WeasyPrint works, an interesting feature showed up. HTML attachments.

They could be declared like this.

<link rel="attachment" href="URL" title="flag">

If WeasyPrint resolved that href, the content would end up embedded inside the final PDF.


Analysis

The key was to separate two different contexts.

  • The backend still needed a public URL to download the HTML.
  • But once that HTML had been downloaded, WeasyPrint resolved additional resources from the server itself.

That made it possible to host a public page and include inside it an attachment pointing to file:///flag.txt. Even though the main URL had to be HTTP, the attached file was resolved locally during PDF generation.

There was no need to force the backend to open file:///flag.txt as the main document. It was enough to make WeasyPrint treat it as an attachment during rendering.


Solution

The malicious page could be as simple as this.

<link rel="attachment" href="file:///flag.txt" title="flag">

The flow was this.

  1. Host that HTML at a public URL.
  2. Send that URL to the conversion service.
  3. Let WeasyPrint generate the PDF.
  4. Download the resulting PDF.
  5. Extract the attachment or inspect the stream until the ENO{...} string appears.

In the final PDF, the contents of /flag.txt were embedded as an attachment, so they could be recovered without reading the file directly from the web server.


Flag

ENO{weasy_pr1nt_can_h4v3_f1l3s_1n_PDF_att4chments!}