Fondamenti di Test di Sicurezza Etico con Python: Una Guida Pratica per Principianti
Capitolo 2 di 11 · aggiornato 08 lug 2026
Python Security Toolkit: Sintassi e Pattern Essenziali
Python Security Toolkit: Sintassi Essenziali e Pattern
Questa pagina presuppone che tu abbia completato i primi passi nella Pagina 1 — variabili, esecuzione di un file .py, e il concetto che Python esegue dall'alto verso il basso. Ora abbiamo bisogno di pattern che utilizzerai effettivamente quando scrivi script per il Lab Wintermute: estrazione di dati da DVWA a 192.0.2.30, parsing dell'output di nmap per 192.0.2.20, gestione degli errori senza che lo script muoia a metà scansione. Tieni questa pagina aperta in un secondo terminale mentre lavori.
Controllo di Flusso Core: Decisioni e Ripetizione
Condizionali — diramazione basata su un test.
# Basic if/elif/else — checking a port state from scan data
port_state = "open" if port_state == "open": print(f"Port is accessible — investigate service")
elif port_state == "filtered": print(f"Firewall or ACL blocking — port may be open behind filter")
else: print(f"Port closed or host unreachable") # Common comparison operators: ==, !=, <, >, <=, >=, 'in' for membership
Perché questo è importante: Una porta marcata come
filterednon è un risultato pulito — è una domanda senza risposta. Il tuo script deve gestire tutti e tre gli stati, non soloopen.
Cicli — ripetizione del lavoro su target o raccolte di dati.
# for loop — iterate over a known collection
targets = ["192.0.2.20", "192.0.2.30"]
for ip in targets: print(f"Scheduling scan for {ip}") # range() — generate number sequences, useful for port ranges
for port in range(1, 1025): # 1 to 1024 inclusive # scan logic here pass # while loop — repeat while condition holds, with explicit exit
attempt = 0
max_attempts = 3
while attempt < max_attempts: # retry logic here attempt += 1 # increment to avoid infinite loop
enumerate() — ottieni indice e valore insieme, essenziale per output numerati.
services = ["ssh", "http", "ftp"]
for idx, svc in enumerate(services, start=1): print(f"{idx}. {svc}")
# Output: 1. ssh, 2. http, 3. ftp
Archiviazione e Strutturazione dei Dati: Liste e Dizionari
Liste — sequenze ordinate e mutabili. Usale per risultati di scansione ordinati o code di target.
# Creating and modifying
open_ports = [22, 80, 443]
open_ports.append(3306) # add to end
open_ports.insert(0, 21) # insert at position # Accessing by position (zero-indexed)
first_port = open_ports[0] # 21
last_port = open_ports[-1] # 3306 # Slicing — extract subsets
low_ports = open_ports[:3] # first three elements
Dizionari — coppie chiave:valore. Usali per dati strutturati di host: l'IP si associa ai risultati.
# Wintermute scan result storage
host_findings = { "192.0.2.20": { "os": "Linux 2.6", "open_ports": [21, 22, 23, 80], "services": {21: "ftp", 22: "ssh", 23: "telnet", 80: "http"} }, "192.0.2.30": { "os": "Unknown", "open_ports": [80, 443], "services": {80: "http", 443: "https"} }
} # Accessing nested data
metasploitable_services = host_findings["192.0.2.20"]["services"]
print(metasploitable_services[80]) # "http" # Safe access with .get() — returns None instead of crashing if key missing
dvwa_os = host_findings.get("192.0.2.30", {}).get("os")
In termini semplici: le liste sono per "ecco una sequenza di cose," i dizionari sono per "ecco cosa so su questa cosa specifica."
I/O su File: Lettura e Scrittura di Dati degli Script
# Reading a file of target IPs — one per line
with open("wintermute_targets.txt", "r") as f: targets = f.read().splitlines() # removes trailing newlines # Writing structured output — append mode preserves previous runs
with open("scan_results.txt", "a") as f: f.write(f"192.0.2.20: ports {open_ports}\n") # CSV-style parsing for tool output
line = "192.0.2.20,80,open,Apache httpd 2.2.8"
parts = line.split(",") # ['192.0.2.20', '80', 'open', 'Apache httpd 2.2.8']
ip, port, state, banner = parts # unpack into variables
Gestione delle Stringhe: Parsing e Codifica di Dati di Sicurezza
Formattazione — tre approcci, le f-string sono preferite per leggibilità.
target = "192.0.2.20"
port = 80 # f-string (Python 3.6+) — most readable for security scripts
url = f"http://{target}:{port}/dvwa/login.php" # .format() — older, still seen in inherited code
url = "http://{}:{}/dvwa/login.php".format(target, port) # % formatting — legacy, avoid in new code
url = "http://%s:%s/dvwa/login.php" % (target, port)
Codifica e Decodifica — i dati di rete arrivano come byte; hai bisogno di testo.
import base64 # Bytes to text — specify encoding explicitly
raw_bytes = b"user:pass"
text = raw_bytes.decode("utf-8") # "user:pass"
back_to_bytes = text.encode("utf-8") # b"user:pass" # Base64 — common in HTTP Basic Auth headers
credentials = "admin:password"
encoded = base64.b64encode(credentials.encode("utf-8"))
# Result: b'YWRtaW46cGFzc3dvcmQ=' decoded = base64.b64decode(encoded).decode("utf-8")
# Back to: "admin:password"
⚠️ Solo uso autorizzato e difensivo. La codifica trasforma i dati; non li protegge. Base64 non è crittografia — chiunque può invertirla. La vedrai negli header HTTP durante l'analisi legittima del traffico nel Lab Wintermute.
Gestione degli Errori: Mantenere gli Script in Vita Durante l'Impegno
Uno script che si blocca sul terzo host di cinquanta spreca il tuo tempo di lab e lascia lacune nella documentazione.
import requests # Basic try/except — catch specific exceptions, not all
try: r = requests.get("http://192.0.2.30/dvwa/", timeout=5) print(r.status_code)
except requests.exceptions.ConnectionError: print("Host unreachable — check VM network or snapshot state")
except requests.exceptions.Timeout: print("Request timed out — target may be overloaded or filtered")
except requests.exceptions.RequestException as e: print(f"Unexpected error: {e}") # finally block — runs regardless, useful for cleanup
finally: print("Scan attempt logged for report")
Pattern comune: avvolgi l'intero corpo del ciclo per-target in try/except, così un target problematico non uccide l'intero batch.
Esecuzione di Strumenti Esterni: subprocess
Il modulo subprocess (libreria standard da Python 2.4, nessuna installazione necessaria) chiama strumenti di sistema e cattura l'output. Sostituisce funzioni più vecchie e meno sicure come os.system().
import subprocess # SAFE pattern: list of arguments, shell=False (default)
# Runs nmap against Metasploitable2; returns bytes
result = subprocess.check_output( ["nmap", "-sV", "-p", "22,80", "192.0.2.20"], stderr=subprocess.DEVNULL, # suppress error noise timeout=60 # don't hang indefinitely
) # Decode bytes to string for parsing
output_text = result.decode("utf-8")
print(output_text) # If command fails, CalledProcessError is raised with .returncode and .output
Perché questo è importante:
shell=Trueti permette di scriverenmap -sV 192.0.2.20come stringa singola, ma permette anche ai metacaratteri della shell nelle variabili di eseguire comandi arbitrari. Nel Lab Wintermute il rischio è basso; in produzione con input utente, è catastrofico. Costruisci l'abitudine della lista di argomenti ora.
| Parametro | Scopo | Default Sicuro |
|---|---|---|
shell=False | Esecuzione diretta tramite OS, non interprete shell | ✓ Mantieni False |
stderr | Dove inviare lo stream di errore; DEVNULL scarta, STDOUT unisce | Varia in base al bisogno |
timeout | Secondi prima di interrompere un comando bloccato | Imposta sempre |
universal_newlines / text=True | Restituisce stringa invece di byte; text=True è Python 3.7+ | Verifica la tua versione di Python |
Richieste HTTP con la Libreria requests
Non nella libreria standard — installa una volta per ambiente:
python -m pip install requests
import requests # GET request to DVWA — basic call
r = requests.get("http://192.0.2.30/dvwa/login.php", timeout=10) # Response inspection
print(r.status_code) # HTTP result: 200, 404, 500, etc.
print(r.headers.get("Server")) # Web server banner, if disclosed # Text vs. binary content
print(r.text[:200]) # Decoded string using r.encoding
print(r.content[:50]) # Raw bytes — check before decoding # Encoding control — force if detection fails
r.encoding = "utf-8" # or "ISO-8859-1" for legacy apps
print(r.text) # JSON parsing — DVWA API endpoints, or tool output
# r.json() raises exception if response isn't valid JSON
try: data = r.json()
except ValueError: data = None # Not JSON, handle accordingly
Riferimento Rapido: Built-in e Conversioni di Tipo
| Funzione | Tipico Uso di Sicurezza |
|---|---|
len(collection) | Conta porte aperte, anomalie nella lunghezza delle risposte |
range(start, stop) | Itera numeri di porta, genera sequenze di test |
enumerate(iterable, start=1) | Output numerato per report |
int(string) | Converte la stringa porta "80" in intero 80 |
str(number) | Converte intero porta in stringa per concatenazione |
bytes(string, encoding) | Prepara dati per trasmissione di rete |
help(object) | Ispeziona modulo o funzione in Python interattivo |
# help() in practice — when you forget a method
help(requests.get) # shows signature and docstring
help(subprocess.check_output) # verify parameters before running
Metterlo Tutto Insieme: Scheletro Minimale di Script di Recon per Wintermute
#!/usr/bin/env python3
import subprocess
import sys TARGET = "192.0.2.20"
PORTS = [21, 22, 23, 25, 80, 443] def scan_port(target, port): """Run minimal TCP probe, return parsed result or None.""" try: # -Pn: skip host discovery (we know it's up in lab) # -p: single port cmd = ["nmap", "-Pn", "-p", str(port), "--open", target] result = subprocess.check_output(cmd, timeout=30) return result.decode("utf-8") except subprocess.CalledProcessError: return None # port closed or filtered except subprocess.TimeoutExpired: return "TIMEOUT" # Main loop with structured storage
findings = {}
for port in PORTS: output = scan_port(TARGET, port) findings[port] = output print(f"Port {port}: {'OPEN' if output else 'closed/filtered'}") # findings dictionary now holds everything for your report
Riferimento Incrociato alle Prossime Pagine
- Pagina 5 (Ricognizione): Espande il wrapper nmap sopra in scoperta completa degli host.
- Pagina 6 (Applicazioni Web): Approfondisce l'uso di
requestsper interazione con form DVWA e gestione sessioni. - Pagina 9 (Malware): I pattern di codifica qui si applicano all'analisi di payload — in modo sicuro, senza esecuzione.
Nota di Samuel dalla panchina: Tengo un
wintermute_utils.pyin ogni cartella di sessione di lab con il patternscan_port()sopra, modificato per esercizio. Riutilizzare il proprio codice testato batte riscrivere da zero — ma solo se capisci cosa fa. Copy-paste senza comprensione produce script che falliscono silenziosamente alle 2 del mattino quando il tuo rollback di snapshot non funziona.