← Back to Writeups
CUSTOMMediumMisc

Spare Me Some Change

XESXOR9/2/20264 min read
#misc#custom#medium#floating_point_precision_attac#ieee754_exploitation

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

  1. buy - Purchase inventory using balance
  2. sell - Sell inventory for balance
  3. travel - Time travel (multiplies everything by 10^15)
  4. flag - Purchase the flag ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The Constraint Problem

To buy the flag, two conditions must be met:

  1. balance >= flag_price (5 x 10^15 after travel)
  2. 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
>>> 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

  1. Buy 0.56 inventory

    • balance: 5.00 - 0.56 = 4.44
    • inventory: 0.56
  2. Time travel (multiply by 10^15)

    • balance: 4,439,999,999,999,999.50
    • inventory: 560,000,000,000,000.06 (floating-point magic!) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
  3. 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!)
  4. Buy flag

    • balance >= 5 x 10^15 (flag_price)
    • inventory >= 0.05 (fee requirement)
    • SUCCESS!

Exploit Code

python
#!/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
#!/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:

  1. Banking systems use fixed-point decimal (e.g., cents as integers)
  2. Cryptocurrency uses integer arithmetic with defined precision
  3. Trading platforms use decimal libraries (Python's decimal module)

The IEEE 754 Problem

python
# Classic floating-point issues
>>> 0.1 + 0.2
0.30000000000000004

>>> 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Mitigation

python
from decimal import Decimal, ROUND_DOWN

# Safe financial calculation
balance = Decimal('5.00')
inventory = Decimal('0.56')
# Operations remain precise

References