Challenge details
Have you ever read files? Hopefully, this will teach you how to read them. Go read the code, the Dockerfile, the fake flag, and the secrets of the universe…and the real flag, of course.
Introduction
This challenge features a binary compiled with Cosmopolitan Libc - a build system that creates “Actually Portable Executable” binaries. There are no protections enabled except for NX.
The binary opens /tmp/cosmofile.txt and provides a menu with options to read from the file or exit. There’s also a hidden backdoor option 7238770 that allows writing up to 0x70 bytes directly to the FILE structure:
1 | if (rax_10 == 0x6e7472) { |
When option 1 is selected, the program calls fread(&buf, 1, 0x1000, rax).
Exploitation
Exploit Cosmopolitan’s fread_unlocked buffering mechanism. When specific conditions are met, fread uses readv() with two iovec structures - one pointing to the user buffer and another to the FILE’s internal buffer. This allows writing to two memory locations simultaneously, enabling arbitrary writes.
Step 1: Stack Leak
By selecting option 1 initially, obtain a stack leak since the buffer isn’t initialized. The leak appears at a fixed offset in the output.
Step 2: Arbitrary Write via Buffering Trick
With the ability to overwrite the FILE structure that is going to be used with fgets, the next step is finding how to turn this into an arbitrary write.
The key is in Cosmopolitan’s fread_unlocked.c. When these conditions are met:
f->bufmode != _IONBF(not unbuffered mode)n < f->size(requested bytes < FILE buffer size)
the function sets up two iovec structures:

Then readv(f->fd, iov, 2) writes to both locations simultaneously.
For this exploit, control these key fields in the FILE structure:
1 | Offset Field Value |
By setting f->buf to the target address and f->size to be larger than the requested amount, fread will:
- Read 0x1000 bytes from stdin to the stack buffer
- Read additional bytes from stdin directly to the target address (
f->buf)
This provides arbitrary write to any address.
Step 3: Ret2syscall
Since the Docker container bind-mounts /srv at /, executing /bin/sh would actually try to execute /srv/bin/sh which doesn’t exist. Instead, read the flag with open, read and write.
Thankfully, pwntools speeds up this process a lot with the rop.call function.
1 | rop.call('open', ['flag.txt', 0]) |
Overwrite the return address of the menu function with the ROP chain.
Proof

Full exploit
1 | #!/usr/bin/python3 |