Page cover

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')  # → 4919

Quick Function Reference Table

Function
Data Type
Use Case

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 INT3

Data 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 items

Filtering & 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 list

Performance & 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.Queue for 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]

Characteristics Summary

Characteristic
list
tuple
dict
set

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-empty

Conditional Expressions

if code == 200:
    status = "open"
elif code == 408:
    status = "timeout"
else:
    status = "closed"

Loops

while

  • Evaluates before each iteration

  • while ... else executes else if 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 break

for

  • Iterates over any iterable

  • Supports enumerate, zip, unpacking

  • for ... else executes else if loop completes normally

for user in users:
    if user.id == target:
        found = user; break
else:
    found = None

Loop Control

Staement
Effect

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 one

Own local scope; avoids leaking loop vars

Pattern Matching (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