DEADFACE CTF | Cursed Library — Pwn Writeup
“Every cursed book leaves a clue. The trick is learning which address to trust.”
Get Tesla’s stories in your inbox
Join Medium for free to get updates from this writer.
Some pwn challenges hand you one clean binary. Cursed Library gives you a huge executable, a supplied libc and loader, and a hint to use pwninit. The path is built around leaks and a stack overflow: recover the PIE base, leak puts from libc, then return through system("/bin/sh") to read the hidden file.
1. Start with the supplied runtime
The challenge archive is about 514 MiB and contains Cursed_Library, libc.so.6, and ld-linux-x86-64.so.2. Keeping the supplied libc and loader together matters: ROP offsets must match the challenge runtime. The challenge description calls out pwninit, a convenient way to set up a patched local copy with those files.
2. Recover the PIE base
The BookOfTheDead option prints a pointer into its own function. In the ELF, that function is at offset 0x1ba1, so the runtime base is straightforward to calculate:
PIE base = leaked BookOfTheDead address - 0x1ba1That gives us the relocated addresses needed for the first ROP stage.
3. Find the overflow
The checkout routine places a 96-byte buffer at [rbp - 0x60] and reads the book name with gets. Since gets does not enforce the buffer size, it overwrites the saved frame pointer and then the saved return address. The layout is:
96 bytes padding | 8 bytes saved RBP | next 8 bytes overwrite RIPSo the saved return address is at offset 104 from the start of the input.
4. Leak libc with the Necronomicon routine
The Necronomicon function prints the resolved puts() address from the GOT. I use a first-stage chain to call it, then return to main for another menu prompt:
ret → Necronomicon → ret → mainThe supplied libc places puts at offset 0x82060, which gives the libc base:
libc base = leaked puts address - 0x82060With PIE and libc bases known, every address in the final chain can be calculated at runtime.
5. Call system(“/bin/sh”)
The second-stage chain uses a pop rdi; ret gadget to pass the /bin/sh string as the first argument to system:
ret = PIE + 0x1b4c
pop rdi; ret = libc + 0x2a9b7
"/bin/sh" = libc + 0x1aaea4
system = libc + 0x54790The final call is system("/bin/sh"). From the shell, reading /app/Necronomicon.txt returns the challenge flag.
Flag
deadface{T#3_L1br4ry_Kn0w5_A11}The leetspeak reads as “The Library Knows All.”
Final thoughts
Cursed Library is a ret2libc exercise wrapped in a noisy artifact. The challenge supplies what we need to keep the runtime consistent; the work is to connect the clues in order: leak PIE, use the unchecked book-name read to take control, leak puts, and calculate the final ROP chain.
Thanks for reading. Follow me on Medium for more CTF writeups.







