Sitemap
Tesla

Tesla

Penetration Tester | Cybersecurity Trainer | Web & Active Directory Security | Bug Bounty Hunter | CTF Player @ 0xCheckM8

DEADFACE CTF | Cursed Library — Pwn Writeup

3 min readOct 20, 2026

--

Press enter or click to view image in full size

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

Press enter or click to view image in full size

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 - 0x1ba1

That 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 RIP

So the saved return address is at offset 104 from the start of the input.

Press enter or click to view image in full size

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 → main

The supplied libc places puts at offset 0x82060, which gives the libc base:

libc base = leaked puts address - 0x82060

With PIE and libc bases known, every address in the final chain can be calculated at runtime.

Press enter or click to view image in full size

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 + 0x54790

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

--

--

Tesla
Tesla

Penetration Tester | Cybersecurity Trainer | Web & Active Directory Security | Bug Bounty Hunter | CTF Player @ 0xCheckM8