priority-queue
b01lersc
Task: a PIE binary implements a min-heap priority queue of heap-allocated strings, but it also mallocs the flag at startup and loses the pointer. Solution: combine a root use-after-free, a 32-byte heap overwrite, fake-chunk creation, and glibc 2.31 tcache poisoning to overlap the hidden flag allocation and print it.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
priority-queue — b01lersc (BCTF 2026)
Overview
ncat --ssl priority-queue.opus4-7.b01le.rs 8443
We are given a menu-driven priority queue of strings. At first glance it looks like a small heap challenge with insert, delete, peek, and edit operations. The important twist is in main(): the program opens flag.txt, allocates 100 bytes for it on the heap, reads the flag there, and then immediately loses the pointer.
So this is not a shell challenge. The goal is to recover an already allocated heap object that contains the flag.
The final exploit from solve.py uses three ideas:
- leak a heap pointer from freed
0x20tcache entries, - infer the hidden flag allocation from the stable startup allocation order,
- poison tcache to allocate a chunk overlapping
flag_ptr - 0x20, then makeputs()print through into the untouched flag string.
Files and environment
Relevant files:
chall— target binarychall.c— source codesolve.py— working exploitlibc.so.6— Debian GLIBC 2.31-13+deb11u13Dockerfile— remote packaging
Binary protections:
- PIE enabled
- Partial RELRO
- NX enabled
- no stack canary
Allocator detail that matters most: the provided libc is glibc 2.31, so tcache has no safe-linking. That makes classic tcache poisoning practical once we can overwrite a freed chunk's next pointer.
Source analysis
The challenge source is short enough to understand fully.
Hidden flag allocation
At startup:
FILE *file = fopen("flag.txt", "r"); if (file) { char *flag = malloc(100); fgets(flag, 100, file); fclose(file); }
The flag is read into a heap chunk, but flag is a local variable and is never stored anywhere global. After main() continues, that heap object still exists, but the program has no reference to it anymore.
...
$ grep --similar
Similar writeups
- [pwn][Pro]pwn10_nosoeasy — No-So-Easy: tcache poison → GOT overwrite— spbctf
- [pwn][Pro]iz_heap_lv1 — BSS-pointer overlap + tcache poisoning— spbctf
- [pwn][Pro]pwn9_mc4 — Mic Check: leak and pwn!— spbctf
- [pwn][Pro]Taste— grodno_new_year_2026
- [pwn][Pro]pwn8_logger — logger_easy!(not) — UAF/alias + tcache poison— spbctf