Spare Me Some Change
Spare Me Some Change
Platform: 0xl4ugh | Category: Misc | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-24 | Status: Solved Techniques: floating_point_precision_attack, ieee754_exploitation
Summary
Task: Trading game with time travel (10^15 multiplier), need balance >= flag_price AND inventory >= 0.05 fee. Solution: Exploit IEEE 754 floating-point precision errors - 0.56 * 10^15 = 560000000000000.06, sell integer part, keep 0.06 as fee.
Spare Me Some Change - 0xL4ugh CTF
Description
Time travel is beautiful, can you make money out of it?
nc spare-change.webctf.online 1337
A trading game challenge where the goal is to accumulate enough balance to purchase a flag while maintaining inventory for the transaction fee.
Initial Analysis
Game State
- Initial balance: $5.00
- Initial inventory: 0.00
- Flag price: $5.00
- Bank fee: $0.05 (deducted from inventory on flag purchase)
Available Actions
buy- Purchase inventory using balancesell- Sell inventory for balancetravel- Time travel (multiplies everything by 10^15)flag- Purchase the flag
The Constraint Problem
To buy the flag, two conditions must be met:
balance >= flag_price(5 x 10^15 after travel)inventory >= 0.05(for the bank fee)
Since balance + inventory = 5 initially, and everything scales proportionally after travel, it appears mathematically impossible to satisfy both conditions simultaneously:
- If you keep all $5 in balance, you have no inventory for the fee
- If you keep $0.05 in inventory, your balance is only $4.95
The Vulnerability
IEEE 754 Floating-Point Precision Errors!
When floating-point numbers are multiplied by large values like 10^15, precision errors accumulate. The challenge name "Spare Me Some Change" is a clever hint - the "spare change" refers to the tiny fractional amounts that appear due to floating-point representation errors.
Key Discovery
Testing various inventory values multiplied by 10^15:
python read-only • copy allowed>>> 0.56 * 1000000000000000 560000000000000.06 # NOT exactly 560000000000000.00!
The fractional part 0.06 is greater than the required 0.05 fee!
Why This Works
In IEEE 754 double-precision floating-point:
- 0.56 cannot be represented exactly in binary
- When multiplied by 10^15, the representation error is magnified
- The result has a small positive fractional component
After selling the integer part of inventory, this "spare change" remains - enough to cover the fee!
Solution
Step-by-Step Exploitation
-
Buy 0.56 inventory
- balance: 5.00 - 0.56 = 4.44
- inventory: 0.56
-
Time travel (multiply by 10^15)
- balance: 4,439,999,999,999,999.50
- inventory: 560,000,000,000,000.06 (floating-point magic!)
-
Sell integer part (560000000000000)
- balance: 4,439,999,999,999,999.50 + 560,000,000,000,000 = 5,000,000,000,000,000.00
- inventory: 0.06 (the "spare change" remains!)
-
Buy flag
- balance >= 5 x 10^15 (flag_price)
- inventory >= 0.05 (fee requirement)
- SUCCESS!
Exploit Code
python read-only • copy allowed#!/usr/bin/env python3 """ 0xL4ugh CTF - Spare Me Some Change Floating-point precision attack on trading game """ import socket import time def interact(s, cmd): """Send command and receive response""" s.send((cmd + '\n').encode()) time.sleep(0.3) return s.recv(4096).decode() def solve(): # Connect to challenge s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('spare-change.webctf.online', 1337)) # Receive banner print(s.recv(4096).decode()) # Step 1: Buy 0.56 inventory print("[*] Buying 0.56 inventory...") print(interact(s, '1')) # Select buy print(interact(s, '0.56')) # Amount # Step 2: Time travel print("[*] Time traveling...") print(interact(s, '3')) # Select travel # Step 3: Sell integer part only print("[*] Selling 560000000000000...") print(interact(s, '2')) # Select sell print(interact(s, '560000000000000')) # Integer part # Step 4: Buy flag print("[*] Buying flag...") response = interact(s, '4') # Select flag print(response) s.close() return response if __name__ == '__main__': solve()
Alternative: Finding Optimal Values
python read-only • copy allowed#!/usr/bin/env python3 """ Find inventory values that produce fractional parts > 0.05 after scaling """ MULTIPLIER = 1000000000000000 # 10^15 FEE = 0.05 for i in range(1, 500): val = i / 100 # Test 0.01 to 4.99 scaled = val * MULTIPLIER fractional = scaled - int(scaled) if fractional > FEE: print(f"{val:.2f} -> {scaled} (fractional: {fractional:.6f})")
Output shows multiple valid values, with 0.56 being one of the simplest.
Lessons Learned
Real-World Implications
This vulnerability demonstrates why floating-point arithmetic should never be used for financial calculations:
- Banking systems use fixed-point decimal (e.g., cents as integers)
- Cryptocurrency uses integer arithmetic with defined precision
- Trading platforms use decimal libraries (Python's
decimalmodule)
The IEEE 754 Problem
python read-only • copy allowed# Classic floating-point issues >>> 0.1 + 0.2 0.30000000000000004 >>> 0.1 + 0.1 + 0.1 - 0.3 5.551115123125783e-17
Mitigation
python read-only • copy allowedfrom decimal import Decimal, ROUND_DOWN # Safe financial calculation balance = Decimal('5.00') inventory = Decimal('0.56') # Operations remain precise
References
- IEEE 754 Floating-Point Standard
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
- Python Decimal Module