Description

When exploiting a program such as a CTF challenge, it is often a good idea to have a standardized starting point. I will refer to this as an exploit script template. My template is inspired by Max Kamper’s exploit script that is used in his Heap Lab turorials on Udemy (which I highly recommend).

Script

I usually have it in ~/Desktop/xpl.py which easily allows me to copy it around my file system whenever I start a new CTF.
Note: Do not name your exploit script ‘pwn.py’ since this will confuse the python interpreter with the pwntools library also named ‘pwn’ and then it wil not work.
You can copy the script from below:

#!/usr/bin/python3
from pwn import *
elf = context.binary = ELF("chall")

gs = '''
c
'''

context.arch = 'amd64'

def start():
    if args.GDB:
        return gdb.debug(elf.path, gdbscript=gs, aslr=False)
    if args.REMOTE:
        return remote("address", 12345)
    else:
        return process(elf.path, close_fds=False)

#shellcode = asm('\n'.join([
    
#]))


io = start()



io.interactive()

Walkthrough - line by line

The following line is called a shebang and tells the system which python interpreter to use:

#!/usr/bin/python3

The following line imports all functions from the pwntools library:

from pwn import *

The following line sets the context binary to the “chall” binary in the same directory:

elf = context.binary = ELF("chall")

The following sets the gdb script where you can set gdb commands as you wish.
For example to break on a specific address you could use: b *addr or b *main+104 depending on your needs:

gs = '''
c
'''

The folling line sets the context.arch variable in pwntools. This is important for when you need shellcode to compile correctly. You can change this depending on the target binary.

context.arch = 'amd64'

The folling function “start()” will be used to start the target binary that is specified at the beginning of the script. Some important things to note are:

  • args.GDB wich means you can start the program with gdb debugging using the command:
    python3 xpl.py GDB
    

    Note: the ‘GDB’ is case sensitive

  • The close_fds=False means that previously opened file descriptors will still be open. This is useful when you need to open some file that should be visible to the target program when it is started. This does not include stdin, stdout or stderr which will be opened by default.
  • aslr=False is an option that allows you to select if you want to debug with or without aslr enabled - even when inside of GDB. This can make your debugging experience easier.
def start():
    if args.GDB:
        return gdb.debug(elf.path, gdbscript=gs, aslr=False)
    if args.REMOTE:
        return remote("address", 12345)
    else:
        return process(elf.path, close_fds=False)

The following can be uncommented if you need to shellcode in your exploit script.

#shellcode = asm('\n'.join([
    
#]))

One example of shellcoding is:

value = 15
shellcode = asm('\n'.join([
    "mov rax, 90",
    f"mov rsi {value}",
    "syscall"
]))

The formatted string above is just an example to show you what’s possible.

The last two lines are used to start the target binary and go to interactive mode.
Your exploit should mainly be written between those to function calls:

io = start()

# Primarily writing exploit here.

io.interactive()