Python Offensive Security 101
Python Fundamentals
Data Types
Arbitrary-Precision Integers int
Python integers have unlimited precision. No 32/64-bit limits.
Common Security Uses:
Addresses, offsets, opcode fields
Encoding binary values (IP, ports)
Cryptographic math (RSA, ECC)
Payload and packet manipulation
Advantages:
Native support for big numbers
Pitfalls:
You must know how many bytes to allocate in
.to_bytes()Large values may crash when packed into small byte fields
EXAMPLE:
n = 0x1337
n.bit_length() # → 13
n.to_bytes(2, 'big') # → b'\x13\x37'
int.from_bytes(b'\x13\x37', 'big') # → 4919Binary Floating Point (IEEE 754) float
64-bit binary floats, prone to rounding errors.
Common Security Uses:
Packet timestamps
Timing-based attacks
Network latency measurements
Pitfalls:
Tiny rounding errors can ruin time-sensitive exploits
EXAMPLE:
0.1 + 0.2 # → 0.30000000000000004 ❌
from math import isclose
isclose(0.1 + 0.2, 0.3) # → True
# Precise Alternatives
from decimal import Decimal
Decimal("0.1") + Decimal("0.2") # → 0.3 (exact)
from fractions import Fraction
Fraction(1, 3) + Fraction(1, 3) # → 2/3Booleans bool
Subclass of int. True == 1, False == 0.
Common Uses:
Network state checks
Execution flags
Guard conditions in exploits
Pitfalls:
Nonemay look falsy but may mean "no value" (not False)
EXAMPLE:
#Falsy values
if 0, if "", if [], if {}, if None # All are False
# Custom truth in clases
def __bool__(self):
return self.is_aliveImmutable Byte Sequences bytes
Raw bytes (0–255), immutable.
Common Security Uses:
Shellcode
Network protocol crafting
Hashing, signatures, binary blobs
Pitfalls:
Cannot modify without creating a copy
EXAMPLE:
b = b"\x90\x90\xcc"
b.hex() # → '9090cc'
bytes.fromhex("9090cc") # → b"\x90\x90\xcc" Mutable Byte Sequences bytearray
Like bytes, but modifiable in-place.
Common Security Uses:
Shellcode patching
Dynamic payloads
Manual field injection in binary headers
Advantages:
You can edit without reallocating
Pitfalls:
Not hashable → cannot use as dictionary keys
EXAMPLE:
payload = bytearray(b"\x90" * 10)
payload[0] = 0xcc # Replace first NOP with INT3Unicode Text str
Unicode strings, used for readable text manipulation.
Common Security Uses:
Shell command composition
Fuzzing input
Regex on logs or files
Filenames, domains, paths
Pitfalls:
String concatenation with
+in loops is slow
EXAMPLE:
# Useful Functions
s.startswith("GET")
s.endswith(".php")
s.find("admin")
s.replace("rm", "echo")
s.split(" ")
"".join(["a", "b", "c"])
# Decode Network Input
text = b"\xe2\x9c\x94".decode("utf-8", errors="replace")Binary Packing & Unpacking struct
Format numbers into raw byte sequences, or extract them.
Common Security Uses:
Building headers (DNS, TCP/IP, etc.)
Parsing binary formats
Exploit payload structure
Pitfalls:
Incorrect byte order = broken payload
Format codes:
!→ Network (big-endian)H→ 2-byte unsigned shortI→ 4-byte unsigned intB→ 1 byte
EXAMPLE:
import struct
pkt = struct.pack("!H", 0x1337) # → b'\x13\x37'
val = struct.unpack("!H", pkt)[0] # → 4919Quick Function Reference Table
int(x, base)
int
Convert string to int (e.g., hex)
int.from_bytes(b, endian)
int ↔ bytes
Bytes → Integer
n.to_bytes(n_bytes, endian)
int ↔ bytes
Integer → Bytes
hex(n)
int
Int → hex string
bytes.fromhex(s)
bytes
Hex string → Bytes
b.hex()
bytes
Bytes → Hex string
bytearray(b)
bytearray
Mutable byte buffer
struct.pack(fmt, ...)
struct
Pack data to binary format
struct.unpack(fmt, b)
struct
Unpack binary to values
isclose(a, b)
float
Approximate float comparison
Decimal("0.1")
decimal
Exact decimal arithmetic
Fraction(1, 3)
fraction
Rational numbers
s.find(sub)
str
Find substring
s.replace(a, b)
str
Replace substrings
" ".join(list)
str
Join list of strings
binascii.hexlify(b)
bytes
Manual hex encoding
base64.b64encode(b)
bytes
Encode payload in base64
hashlib.sha256(b).hexdigest()
hashlib
SHA-256 hash for binary
Mini CheatSheet Snippets
# Int ↔ Bytes
i = int.from_bytes(b"\xde\xad", 'big')
b = i.to_bytes(2, 'big')
# Shellcode from hex
shellcode = bytes.fromhex("fc4883e4f0e8")
# DNS packet header
import struct
header = struct.pack("!HHHHHH", 0x1337, 0x0100, 1, 0, 0, 0)
# Patch shellcode
buf = bytearray(b"\x90" * 8)
buf[0] = 0xcc # Change first NOP to INT3Data Structures
Lists list
Lists = mutable, ordered containers, great for short-lived target batches, scan results, and small queues. Don’t use them as huge persistent stores.
Basic Ops
ports = [21, 22, 80, 443] # create
ports.append(8080) # add
ports[0] = 2121 # modify
del ports[1] # delete
len(ports) # count itemsFiltering & Transforming
open_ports = [p for p in ports if is_open(p)] # fast filter
targets = [(ip,p) for ip in hosts for p in ports] # combine
g = (p for p in all_ports if is_open(p)) # generator (less memory)Stacks & Queues
# Stack (LIFO)
stack.append(x); x = stack.pop()
# Queue (FIFO)
from collections import deque
q = deque(); q.append(x); x = q.popleft()Use deque instead of list.pop(0) for performance.
Slicing & Copying
first3 = ports[:3] # shallow copy
rev = ports[::-1] # reverse
ports.sort() # in-place sort
sorted_ports = sorted(ports) # new listPerformance & Memory
Each slot stores a pointer (~8 bytes).
Use
array,numpy, or generators for large numeric data.list.clear()reuses memory instead of reallocating.
Security & Concurrency
Not thread-safe → use
queue.Queuefor parallel scans.Don’t keep secrets in lists. Clear (
clear()/del) sensitive data after use.Serialize results carefully (avoid leaking credentials or tokens).
EXAMPLE:
ports = [22, 80, 443]
# filter open ports
open_ports = [p for p in ports if is_open(p)]
# Example output (if 22 and 443 are open):
# open_ports = [22, 443]Tuples tuple
Tuples = immutable, ordered containers. Great for storing coordinates, IP/port pairs, or any read-only sequences in scans and pipelines.
Basic Ops
coords = (10.0, 20.5)
ip_port = ("10.0.0.5", 22)
len(coords) # 2
coords[0] # 10.0Immutable → cannot append, delete, or change elements.
Common Use Cases
Keys in dicts or sets → hashable and safe
Return multiple values from a function:
return ip, port, statusRead-only sequences → e.g., default headers, config tuples
Unpacking Tricks
host, port = ip_port # simple unpack
a, *middle, z = range(10) # starred unpackingHandy for extracting first/last elements or splitting ranges in scanners
Namedtuple / Dataclass
from collections import namedtuple
Result = namedtuple("Result", "ip port status")
r = Result("10.0.0.5", 80, "open")
r.ip # "10.0.0.5"
r.port # 80Gives attribute access
Immutable & memory-light
Perfect for storing scan results, port statuses, or target info
EXAMPLE
def scan_target(ip, ports):
results = []
for p in ports:
status = "open" if is_open(ip,p) else "closed"
results.append(Result(ip, p, status))
return results
res = scan_target("10.0.0.5", [22,80,443])
# Example output (repr of namedtuples):
# [
# Result(ip='10.0.0.5', port=22, status='open'),
# Result(ip='10.0.0.5', port=80, status='closed'),
# Result(ip='10.0.0.5', port=443, status='open')
# ]Dictionaries dict
Dicts = mutable, key-value stores . Ideal for mapping services, ports, hosts, scan results.
Basic Ops
services = {"ssh": 22, "http": 80, "https": 443}
services["dns"] = 53 # add/update
port = services.get("ftp", 21) # get with defaultIteration View
for key, value in services.items():
print(key, value)
# items(), keys(), values() are dynamic views reflecting mutationsUseful Variants
from collections import defaultdict, Counter
# Auto-create lists: group hosts by ASN
hosts_by_asn = defaultdict(list)
hosts_by_asn[123].append("10.0.0.1")
# Count frequencies: e.g., top user-agents
ua_count = Counter(["curl","curl","python-requests"])
# OrderedDict (order guaranteed + move_to_end)
from collections import OrderedDict
od = OrderedDict()
od["first"] = 1
od["second"] = 2
od.move_to_end("first") # move key to endSecurity Note
Hash-flood attacks: huge sets of colliding keys can slow lookups to O(n).
CPython 3.3+ uses random hash seeds to mitigate this, still relevant when fuzzing protocols or parsing untrusted input.
EXAMPLE:
services = {"ssh":22,"http":80,"https":443}
services["dns"] = 53
print(services.items())
# Output (order may vary):
# dict_items([('ssh', 22), ('http', 80), ('https', 443), ('dns', 53)])Sets set
Sets = unordered, mutable collections for fast membership and deduplication. Ideal for pruning wordlists and tracking visited hosts.
Basic Ops
seen = {"10.0.0.5","10.0.0.7"}
seen.add("10.0.0.8")
"10.0.0.5" in seen # True
seen.discard("1.2.3.4") # no error if missing
# seen.remove(x) raises if x not presentUseful methods
seen.pop() # remove arbitrary item
seen.clear() # empty set
A.issubset(B); A.issuperset(B)
len(seen)Comprehension & math ops
uniq = {x for x in wordlist if valid(x)}
intersection = A & B
difference = B - A
union = A | B
symmetric = A ^ BUse cases (offsec)
Prune huge wordlists (
uniq = set(wordlist))Track visited nodes in graph/bfs/dfs
Fast membership checks before expensive probes
Perf & security notes
Membership = O(1) average.
Sets use hashing like dicts → vulnerable conceptually a hash-collision flood (same mitigations as dicts).
Prefer sets for memory-light dedup of moderate-size lists; for huge datasets consider Bloom filters or disk-backed DB.
Characteristics Summary
Mutability
Yes
No
Yes
Yes
Ordering
Yes
Yes
Yes
No
Allows Duplicates
Yes
Yes
Keys unique, values can duplicate
No
Hashable
No
Yes
Keys only
Yes
Typical Use (offsec)
Target batches, scan results, stacks/queues
IP/port pairs, coordinates, read-only constants
Map service→port, results keyed by host/IP
Dedup targets, fast membership checks, visited nodes
Control Flow
Boolean Contexts
Any object can be tested for truth:
False:
False, None, 0, 0.0, "", [], {}, set()True: everything else
if items: process(items) # executes if list non-emptyConditional Expressions
if code == 200:
status = "open"
elif code == 408:
status = "timeout"
else:
status = "closed"Loops
while
Evaluates before each iteration
while ... elseexecuteselseif loop exits normally (no break)
ports = [22, 80, 443, 8080]
target = "10.0.0.5"
i = 0
while i < len(ports):
port = ports[i]
if is_open(target, port):
print(f"{target}:{port} -> OPEN")
break # stop at first open port (optional)
i += 1
else:
print(f"No open ports found on {target}") # only runs if no breakfor
Iterates over any iterable
Supports
enumerate,zip, unpackingfor ... elseexecuteselseif loop completes normally
for user in users:
if user.id == target:
found = user; break
else:
found = NoneLoop Control
break
Exit innermost loop immediately
continue
Skip rest of iteration, continue loop
pass
Do nothing (placeholder)
Comprehensions
Build lists, sets, dicts, generators concisely
# List comprehension: build list of target ports to scan
ports = [22, 80, 443, 8080]
open_ports = [p for p in ports if is_open("10.0.0.5", p)]
# Example output (if 22 and 443 are open):
# open_ports = [22, 443]
# Set comprehension: deduplicate discovered hosts
hosts = ["10.0.0.1","10.0.0.2","10.0.0.1"]
uniq_hosts = {h for h in hosts}
# uniq_hosts = {'10.0.0.1','10.0.0.2'}
# Dict comprehension: invert service→port mapping
services = {"ssh":22,"http":80,"https":443}
port_to_service = {v:k for k,v in services.items()}
# port_to_service = {22:'ssh', 80:'http', 443:'https'}
# Generator comprehension: lazy evaluation for scanning huge IP ranges
targets = [f"10.0.0.{i}" for i in range(1,255)]
gen_scan = (ip for ip in targets if is_alive(ip))
# yields live hosts one by oneOwn local scope; avoids leaking loop vars
Pattern Matching (match ... case) 3.10+
match ... case) 3.10+Multi-way branch with destructuring
match command.split():
case ["GET", path]: handle_get(path)
case ["POST", path, data]: handle_post(path, data)
case _: print("Unknown")Last updated
