Pwntools
Pwntools is a CTF (Capture The Flag) framework and exploit development library for Python. It provides a comprehensive set of tools for writing exploits, interacting with binaries and remote services, performing assembly/disassembly, ROP chain generation, and much more. The current version is 4.15.0, and it maintains an active release cadence.
Common errors
-
ImportError: No module named pwn
cause The pwntools library (specifically the 'pwn' module) is not installed or not accessible in your current Python environment.fixInstall pwntools using pip: `pip install pwntools`. If using virtual environments, ensure your environment is activated. -
TypeError: 'str' does not support the buffer interface
cause You are attempting to send a Python 3 `str` object to a pwntools function (like `io.send()`, `io.sendline()`, or `asm()`) that explicitly expects a `bytes` object.fixConvert your string to bytes using `.encode()` or use a byte literal: `io.sendline('my string'.encode('utf-8'))` or `io.sendline(b'my string')`. -
pwnlib.exception.PwnlibException: Unknown architecture <ARCH_NAME>
cause The `context.arch` variable is either unset or set to an invalid/unsupported architecture string when an architecture-dependent function (like `asm()` or `ELF().disasm()`) is called.fixSet `context.arch` to a valid architecture string at the beginning of your script, e.g., `context.arch = 'amd64'` or `context.arch = 'arm'`. Consult pwntools documentation for supported architecture names. -
socket.gaierror: [Errno -2] Name or service not known
cause The hostname or IP address provided to `pwn.remote(host, port)` is invalid, misspelled, or cannot be resolved by your system's DNS.fixVerify that the `host` string in your `remote()` call is correct and resolvable. Check for typos. Ensure you have an active network connection. -
IndexError: cyclic_find could not find pattern
cause The `cyclic_find()` function could not locate the specific unique pattern (often used to find offsets in buffer overflows) within the provided input.fixEnsure the pattern you are searching for is exactly present in the input you're examining (e.g., from a crash dump). The `cyclic` pattern generated might be too short, or the offset is outside the range searched.
Warnings
- breaking Pwntools has dropped official support for Python 2. While older versions worked with Python 2, current versions (4.x and above) are Python 3 only. Many older online tutorials or code snippets might be for Python 2, leading to syntax errors or unexpected behavior in Python 3.
- gotcha Failure to correctly set `context.arch` and `context.os` can lead to incorrect assembly/disassembly, packing/unpacking (e.g., `p64`, `u64`), ROP chain generation, or shellcode execution. Pwntools defaults to `i386` and `linux` if not specified, which may not match your target.
- gotcha The common practice `from pwn import *` imports all public symbols from the `pwn` module directly into your script's namespace. This can lead to name collisions with other variables or functions defined in your script or other imported modules, making debugging harder.
- gotcha Python 3 differentiates strictly between `str` (unicode text) and `bytes` (sequence of bytes). IO functions like `send`, `sendline`, `recv`, `recvline` in pwntools expect and return `bytes`. Mixing `str` with `bytes` without explicit encoding/decoding will raise `TypeError`.
Install
-
pip install pwntools
Imports
- pwn
import pwntools; from pwntools import pwn
from pwn import *
- remote
from pwn import remote
- process
from pwn import process
- ELF
from pwn import ELF
- ROP
from pwn import ROP
- asm
from pwn import asm
- log
from pwn import log
Quickstart
from pwn import *
# Configure global context for architecture and OS (important for assembly/disassembly, packing)
context.arch = 'amd64' # Example: ARM, i386, amd64
context.os = 'linux' # Example: windows, freebsd
context.log_level = 'info' # Debug, info, warn, error, critical
# --- Example: Interact with a remote service ---
# Replace with the actual challenge host and port
HOST = 'challenge.example.com'
PORT = 1337
try:
log.info(f"Connecting to {HOST}:{PORT}...")
# Establish a connection to the remote service
io = remote(HOST, PORT)
log.success("Connected!")
# Receive initial data (e.g., a banner)
banner = io.recvline()
log.info(f"Received banner: {banner.decode(errors='ignore').strip()}")
# Send some input (e.g., a simple payload for a buffer overflow)
# pwntools handles bytes automatically for send/recv
payload = b'A' * 72 + p64(0xdeadbeef) # 72 bytes of 'A', then an 8-byte address
io.sendline(payload)
log.info(f"Sent payload: {payload!r}")
# Receive the response after sending data
response = io.recvall()
log.info(f"Received full response: {response.decode(errors='ignore').strip()}")
io.close()
log.success("Connection closed.")
except PwnlibException as e:
log.error(f"Pwntools error: {e}")
except Exception as e:
log.error(f"General error: {e}")