Use lists, dictionaries, sets, and tuples for security data organization
Implement control flow (if/for/while) for automated security tasks
Parse and analyze security logs programmatically
Build a multi-target vulnerability scanner with result tracking
Create password validation tools with complex rule enforcement
1) Strings: The Language of Security Data
In cybersecurity, almost everything is a string: log entries, HTTP responses, exploit payloads,
configuration files, and network traffic. Mastering string manipulation is essential.
String Operations for Security
# Log entry from a web server
log_entry = '192.168.1.100 - - [18/Jan/2024:10:15:23] "GET /admin HTTP/1.1" 403 512'
# Extracting information
if "403" in log_entry or "404" in log_entry:
print("⚠️ Client error detected")
# Splitting on delimiters
parts = log_entry.split('" "')
request = parts[0].split(']')[1].strip() # "GET /admin HTTP/1.1"
status_code = parts[1].split()[0] # "403"
# Finding substrings
ip_start = 0
ip_end = log_entry.find(" - -")
ip_address = log_entry[ip_start:ip_end] # "192.168.1.100"
print(f"IP: {ip_address}, Request: {request}, Status: {status_code}")
Common String Methods
# Case manipulation (important for comparison)
username = "Admin"
if username.lower() == "admin": # Case-insensitive comparison
print("Admin user detected")
# Whitespace handling
dirty_input = " user@example.com \n"
clean_input = dirty_input.strip() # "user@example.com"
# Replacement (payload generation, sanitization)
unsafe_query = "SELECT * FROM users WHERE name=''"
payload = unsafe_query.replace("", "admin' OR '1'='1")
# Checking prefix/suffix (file type validation)
filename = "malware.exe"
if filename.endswith((".exe", ".dll", ".bat")):
print("⚠️ Executable file detected")
# Splitting and joining
ports = "80,443,8080,8443"
port_list = ports.split(",") # ['80', '443', '8080', '8443']
formatted = " | ".join(port_list) # "80 | 443 | 8080 | 8443"
F-Strings for Payload Construction
# Modern Python 3.6+ f-strings (preferred method)
target = "10.0.0.5"
port = 445
protocol = "SMB"
# Clean and readable
message = f"Scanning {target}:{port} ({protocol})"
# Expression evaluation inside f-strings
cvss_score = 7.5
severity = f"CVSS {cvss_score} - {'CRITICAL' if cvss_score >= 9 else 'HIGH' if cvss_score >= 7 else 'MEDIUM'}"
# SQL injection payload generation
username = "admin"
sqli_payloads = [
f"' OR '1'='1",
f"' OR 1=1--",
f"admin'--",
f"' UNION SELECT NULL,NULL,NULL--"
]
Raw Strings for Regex and Paths
# Regular string - backslashes are escape characters
path = "C:\\Users\\Admin\\Documents" # Need to escape \
# Raw string - backslashes are literal
path = r"C:\Users\Admin\Documents" # Much cleaner
# Essential for regex patterns (we'll cover regex in Week 4)
ip_pattern = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
2) Lists: Sequential Security Data
Lists store ordered collections—perfect for targets, scan results, wordlists, and vulnerability
findings.
Creating and Manipulating Lists
# Common security uses
targets = ["192.168.1.1", "192.168.1.5", "192.168.1.10"]
common_ports = [21, 22, 23, 25, 80, 443, 3389, 8080]
vulnerabilities = [] # Start empty, populate during scan
# Adding items
vulnerabilities.append({"type": "SQL Injection", "cvss": 9.0})
vulnerabilities.append({"type": "XSS", "cvss": 6.5})
# Extending lists
more_targets = ["10.0.0.1", "10.0.0.2"]
targets.extend(more_targets) # Adds all items
# Inserting at specific position
common_ports.insert(0, 20) # FTP data port at beginning
# Removing items
common_ports.remove(20) # Remove by value
last_port = common_ports.pop() # Remove and return last item
del common_ports[0] # Remove by index
# Traditional loop
open_ports = []
for port in range(1, 1001):
if scan_port(host, port):
open_ports.append(port)
# List comprehension (more Pythonic)
open_ports = [port for port in range(1, 1001) if scan_port(host, port)]
# Filtering with conditions
high_severity = [v for v in vulnerabilities if v["cvss"] >= 7.0]
# Transforming data
ip_list = ["192.168.1.1", "192.168.1.2", "192.168.1.3"]
with_ports = [f"{ip}:80" for ip in ip_list]
# ['192.168.1.1:80', '192.168.1.2:80', '192.168.1.3:80']
# Nested comprehensions (advanced)
subnet_scan = [f"192.168.{subnet}.{host}"
for subnet in range(1, 4)
for host in range(1, 11)]
Sorting and Searching
# Sorting lists
ports = [443, 80, 22, 3389, 8080]
ports.sort() # Sorts in-place: [22, 80, 443, 3389, 8080]
ports.sort(reverse=True) # Descending
# Sorting with custom key
vulns = [
{"name": "XSS", "cvss": 6.5},
{"name": "SQLi", "cvss": 9.0},
{"name": "CSRF", "cvss": 5.0}
]
sorted_vulns = sorted(vulns, key=lambda v: v["cvss"], reverse=True)
# Highest CVSS first
# Searching
if 80 in ports:
print("HTTP port is in list")
index = ports.index(443) # Find position of 443
# Counting occurrences
protocols = ["TCP", "UDP", "TCP", "TCP", "ICMP"]
tcp_count = protocols.count("TCP") # 3
3) Dictionaries: Key-Value Security Mappings
Dictionaries are Python's hash tables—perfect for scan results, configuration, API responses, and
vulnerability databases.
Sets store unique items—perfect for deduplication and set operations.
# Deduplicate IPs from logs
log_ips = ["10.0.0.1", "10.0.0.2", "10.0.0.1", "10.0.0.3", "10.0.0.2"]
unique_ips = set(log_ips) # {'10.0.0.1', '10.0.0.2', '10.0.0.3'}
# Set operations (comparing scan results)
scan1_ports = {21, 22, 80, 443}
scan2_ports = {22, 80, 443, 3389, 8080}
# Intersection (in both scans)
common = scan1_ports & scan2_ports # {22, 80, 443}
# Union (in either scan)
all_ports = scan1_ports | scan2_ports # {21, 22, 80, 443, 3389, 8080}
# Difference (in scan1 but not scan2)
scan1_only = scan1_ports - scan2_ports # {21}
# Fast membership testing
if 3389 in scan2_ports:
print("RDP port found")
Tuples: Immutable Sequences
Tuples are like lists but cannot be modified—useful for constants and dictionary keys.
# Common port definitions (won't change)
WELL_KNOWN_PORTS = (
(20, "FTP Data"),
(21, "FTP Control"),
(22, "SSH"),
(23, "Telnet"),
(25, "SMTP"),
(80, "HTTP"),
(443, "HTTPS")
)
# Tuple unpacking
for port, service in WELL_KNOWN_PORTS:
print(f"{service}: {port}")
# Using tuples as dictionary keys (lists can't be keys)
scan_results = {
("192.168.1.1", 80): "OPEN",
("192.168.1.1", 443): "OPEN",
("192.168.1.5", 22): "CLOSED"
}
# Named tuples (more readable)
from collections import namedtuple
ScanResult = namedtuple("ScanResult", ["ip", "port", "status", "service"])
result = ScanResult("192.168.1.1", 80, "OPEN", "HTTP")
print(f"{result.ip}:{result.port} is {result.status} ({result.service})")
5) Control Flow: Automating Security Decisions
If Statements: Risk-Based Decision Making
# CVSS-based severity classification
cvss_score = 8.5
if cvss_score >= 9.0:
severity = "CRITICAL"
action = "Patch immediately"
color = "\033[91m" # Red
elif cvss_score >= 7.0:
severity = "HIGH"
action = "Patch within 7 days"
color = "\033[93m" # Yellow
elif cvss_score >= 4.0:
severity = "MEDIUM"
action = "Patch within 30 days"
color = "\033[94m" # Blue
else:
severity = "LOW"
action = "Patch at next maintenance window"
color = "\033[92m" # Green
print(f"{color}[{severity}] CVSS {cvss_score}: {action}\033[0m")
# Compound conditions (authentication checks)
username = "admin"
password = "P@ssw0rd123!"
is_from_trusted_ip = True
failed_attempts = 0
if username == "admin" and password == "admin":
print("❌ Default credentials - BLOCK")
elif failed_attempts >= 5:
print("❌ Account locked - too many failures")
elif not is_from_trusted_ip:
print("⚠️ Login from untrusted IP - require MFA")
else:
print("✅ Authentication successful")
For Loops: Iterating Over Security Data
# Scanning multiple targets
targets = ["192.168.1.1", "192.168.1.5", "192.168.1.10"]
ports = [22, 80, 443]
print("Starting network scan...")
for target in targets:
print(f"\nScanning {target}...")
for port in ports:
result = scan_port(target, port)
status = "OPEN" if result else "CLOSED"
print(f" Port {port}: {status}")
# Using enumerate (when you need index)
vulns = ["SQLi", "XSS", "CSRF", "SSRF"]
for index, vuln in enumerate(vulns, start=1):
print(f"{index}. {vuln}")
# Range for numeric sequences
print("Scanning ports 1-1024...")
for port in range(1, 1025):
if scan_port(target, port):
print(f"[+] Port {port} is OPEN")
# Dictionary iteration
scan_results = {
"10.0.0.1": {"ports": [80, 443], "os": "Linux"},
"10.0.0.2": {"ports": [22, 3389], "os": "Windows"}
}
for ip, details in scan_results.items():
open_count = len(details["ports"])
print(f"{ip} ({details['os']}): {open_count} open ports")
# break: Exit loop immediately
passwords = ["password", "admin", "correct_password", "123456"]
for pwd in passwords:
if try_login(username, pwd):
print(f"✅ Password found: {pwd}")
break # Stop trying more passwords
else:
# This runs if loop completes without break
print("❌ No valid password found")
# continue: Skip to next iteration
for ip in targets:
if ip.startswith("127."): # Skip localhost
continue
scan(ip)
# pass: Placeholder (do nothing)
def advanced_exploit():
pass # TODO: Implement later
for port in range(1, 65536):
# Scanning all ports takes too long, placeholder for now
pass
6) Real-World Security Examples
Example 1: Log Parser with Threat Detection
#!/usr/bin/env python3
"""
Security log analyzer - detects brute force attempts
"""
def analyze_logs(log_file):
"""
Analyze authentication logs for suspicious activity.
Returns:
dict: IP addresses with failed attempt counts
"""
failed_attempts = {}
suspicious_ips = []
with open(log_file, 'r') as f:
for line in f:
if "Failed password" in line or "authentication failure" in line:
# Extract IP address (simplified - real regex in Week 4)
parts = line.split("from")
if len(parts) > 1:
ip = parts[1].split()[0].strip()
# Count failures per IP
failed_attempts[ip] = failed_attempts.get(ip, 0) + 1
# Flag IPs with >5 failures
if failed_attempts[ip] > 5 and ip not in suspicious_ips:
suspicious_ips.append(ip)
print(f"⚠️ ALERT: {ip} has {failed_attempts[ip]} failed attempts")
return failed_attempts, suspicious_ips
# Usage
if __name__ == "__main__":
attempts, suspicious = analyze_logs("/var/log/auth.log")
print(f"\n📊 Summary:")
print(f"Total unique IPs with failures: {len(attempts)}")
print(f"Suspicious IPs (>5 failures): {len(suspicious)}")
print(f"\nTop offenders:")
sorted_ips = sorted(attempts.items(), key=lambda x: x[1], reverse=True)
for ip, count in sorted_ips[:10]:
print(f" {ip}: {count} attempts")
Example 2: Multi-Target Port Scanner with Progress
#!/usr/bin/env python3
"""
Advanced port scanner with multiple targets and progress tracking
"""
import socket
from datetime import datetime
def scan_port(host, port, timeout=1):
"""Check if a single port is open."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((host, port))
sock.close()
return result == 0
except:
return False
def scan_host(host, ports):
"""
Scan multiple ports on a single host.
Returns:
dict: Scan results with open/closed status
"""
results = {"host": host, "open": [], "closed": [], "timestamp": str(datetime.now())}
total = len(ports)
for index, port in enumerate(ports, 1):
# Progress indicator
progress = (index / total) * 100
print(f"\r[{progress:5.1f}%] Scanning {host}:{port}...", end="", flush=True)
if scan_port(host, port):
results["open"].append(port)
else:
results["closed"].append(port)
print() # New line after progress
return results
def scan_network(targets, ports):
"""Scan multiple targets."""
all_results = {}
print(f"🔍 Scanning {len(targets)} targets on {len(ports)} ports...")
print(f"Started: {datetime.now()}\n")
start_time = datetime.now()
for target in targets:
print(f"\n--- Scanning {target} ---")
results = scan_host(target, ports)
all_results[target] = results
# Summary
print(f"✅ Open ports: {results['open'] if results['open'] else 'None'}")
elapsed = (datetime.now() - start_time).total_seconds()
print(f"\n⏱️ Total scan time: {elapsed:.2f} seconds")
return all_results
if __name__ == "__main__":
# Example usage
targets = ["scanme.nmap.org", "example.com"]
common_ports = [21, 22, 23, 80, 443, 3389, 8080]
results = scan_network(targets, common_ports)
# Find all hosts with port 80 open
web_servers = [host for host, data in results.items() if 80 in data["open"]]
print(f"\n🌐 Web servers found: {web_servers}")
Example 3: Password Strength Validator
#!/usr/bin/env python3
"""
Enterprise password policy enforcer
"""
def check_password_strength(password, username="", company=""):
"""
Validate password against security policy.
Returns:
tuple: (score out of 10, list of issues, list of suggestions)
"""
score = 0
issues = []
suggestions = []
# Length check
if len(password) < 8:
issues.append("Password is too short (minimum 8 characters)")
suggestions.append("Use at least 8 characters")
elif len(password) >= 12:
score += 2
elif len(password) >= 8:
score += 1
# Character variety
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_digit = any(c.isdigit() for c in password)
has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)
if has_upper:
score += 2
else:
issues.append("No uppercase letters")
suggestions.append("Add uppercase letters (A-Z)")
if has_lower:
score += 2
else:
issues.append("No lowercase letters")
suggestions.append("Add lowercase letters (a-z)")
if has_digit:
score += 2
else:
issues.append("No numbers")
suggestions.append("Add numbers (0-9)")
if has_special:
score += 2
else:
issues.append("No special characters")
suggestions.append("Add special characters (!@#$%)")
# Common password check
common_passwords = {
"password", "admin", "123456", "qwerty", "letmein",
"welcome", "monkey", "dragon", "master", "sunshine"
}
if password.lower() in common_passwords:
score = 0
issues.append("This password is in the top 100 most common passwords!")
suggestions.append("Use a unique, hard-to-guess password")
# Check if password contains username or company name
if username and username.lower() in password.lower():
score -= 2
issues.append(f"Password contains username '{username}'")
suggestions.append("Don't use your username in password")
if company and company.lower() in password.lower():
score -= 2
issues.append(f"Password contains company name '{company}'")
suggestions.append("Don't use company name in password")
# Sequential characters check
if "123" in password or "abc" in password.lower():
score -= 1
issues.append("Contains sequential characters")
suggestions.append("Avoid sequential patterns (123, abc)")
# Ensure score is between 0-10
score = max(0, min(10, score))
return score, issues, suggestions
# Test the validator
if __name__ == "__main__":
test_passwords = [
("admin", "admin", "AcmeCorp"),
("Password1!", "john", "AcmeCorp"),
("MyC0mp@nyP@ss", "john", "AcmeCorp"),
("xK9#mP2!qL5@", "john", "AcmeCorp")
]
for pwd, user, company in test_passwords:
score, issues, suggestions = check_password_strength(pwd, user, company)
print(f"\n{'='*60}")
print(f"Password: {'*' * len(pwd)} ({len(pwd)} chars)")
print(f"Strength: {score}/10", end="")
if score >= 8:
print(" ✅ STRONG")
elif score >= 6:
print(" ⚠️ MEDIUM")
else:
print(" ❌ WEAK")
if issues:
print(f"\n❌ Issues found ({len(issues)}):")
for issue in issues:
print(f" - {issue}")
if suggestions:
print(f"\n💡 Suggestions:")
for suggestion in suggestions:
print(f" - {suggestion}")
📝 Lab 2: Security Data Processing
Part 1: Multi-Target Scanner (45 minutes)
Objective: Build a production-ready scanner that tracks results across multiple
targets.
Requirements:
Create lab2_network_scanner.py
Accept multiple target IPs from command line OR from a file