A session replay attack lab for defenders
A self-contained, Docker-based security lab that demonstrates how session cookie theft bypasses passwords, 2FA, and even Google OAuth.
Part of CyberDesserts - Learn Cybersecurity By Doing (GitHub)
This lab shows you how stolen browser cookies let attackers bypass passwords and 2FA. You will log in with full security (password + TOTP, or Google OAuth), steal your own session cookie, and replay it to impersonate yourself without any credentials. Then you will switch to a hardened version of the same app and see exactly which defences block the attack.
git clone https://github.com/cyberdesserts/cookiejar-lab.git
cd cookiejar-lab
docker compose up --build
Then open:
You need Docker and Docker Compose installed. A TOTP authenticator app (Google Authenticator, Authy, etc.) is optional but recommended for the full 2FA experience.
Step 1 - Register
Open the vulnerable app (http://localhost:3001 or https://cookiejar.test:3001 if using HTTPS) and create an account with username + password. You’ll see a QR code - scan it with Google Authenticator or Authy to set up TOTP 2FA.
Step 2 - Log in with 2FA Enter your username, password, and the 6-digit TOTP code from your authenticator app. You’ve now authenticated with “strong” multi-factor authentication.
Step 3 - Visit the Cookie Vault Click “Cookie Vault” on the dashboard. This page shows:
Step 4 - Copy the token Click “Copy Cookie to Clipboard”. This simulates what infostealer malware does when it reads Chrome’s cookie SQLite database.
Step 5 - Replay the attack Open the “Attack Console” and paste the stolen token. Click “Replay Session”. You’ll see:
Step 6 - Try Google OAuth Log out and sign in with “Google OAuth” (simulated). Pick any account on the mock consent screen. Visit Cookie Vault again - the session token has the same structure and same vulnerability.
Step 7 - Replay the OAuth session Copy the OAuth session token, paste it into the Attack Console. Same result: authentication bypassed. Google OAuth protected the login step, not the session.
Step 8 - Register and log in
Open the hardened app (http://localhost:3002 or https://cookiejar.test:3002 if using HTTPS). Create an account with the same flow. Log in.
Step 9 - Try to read the cookie
On the dashboard, notice that document.cookie returns an empty string. The session cookie has httpOnly: true - JavaScript cannot access it at all.
Step 10 - Attempt replay Go to the Attack Console. Try pasting a token from the vulnerable app. The hardened app shows a defence-by-defence breakdown of why each control blocks the replay:
Step 11 - Check the session log Click “Session Event Log” on the dashboard to see the audit trail - every replay attempt is logged with IP, User-Agent, and timestamp.
Authentication (password, 2FA, OAuth) only protects the login step. After successful authentication, the server issues a session token - typically a JWT or opaque token stored in a cookie. For subsequent requests, the server checks the session token, not the original credentials.
Login: User -> [Password + 2FA] -> Server -> Session Cookie
Later: User -> [Session Cookie] -> Server -> "You're authenticated"
Attacker: Attacker -> [Stolen Cookie] -> Server -> "You're authenticated"
The server cannot distinguish between the legitimate user presenting the cookie and an attacker presenting the same cookie.
Many people assume Google OAuth makes them “more secure” against this attack. Here’s what actually happens:
Step 5 is the critical point: the session cookie issued by your app is no different from one issued after a password login. Google OAuth protects the authentication with Google - it does not protect the session with your app.
Infostealers don’t attack Google. They attack your browser’s cookie store and steal the session cookie your app issued after OAuth completed.
| Defence | How It Helps | Standard |
|---|---|---|
| HttpOnly cookies | Prevents JavaScript access to cookies | OWASP |
| Short-lived tokens | Limits replay window | NIST 800-63B |
| Server-side sessions | Enables revocation and logout | OWASP |
| Device-bound sessions | Ties sessions to specific devices | DBSC (Chrome) |
| FIDO2/Passkeys | Phishing-resistant, device-bound authentication | FIDO Alliance |
| Token binding | Cryptographically binds tokens to TLS connections | RFC 8471 |
| Continuous auth signals | Risk-based session evaluation | Zero Trust |
cookiejar-lab/ <- repository root
├── README.md # Repo landing page
├── .gitignore # Root-level ignores
├── demo/
│ └── index.html # Standalone interactive demo (no setup needed)
└── cookiejar-lab/
├── .gitignore
├── README.md # Full lab documentation (this file)
├── LICENSE
├── setup.sh # HTTPS setup
├── cleanup.sh # HTTPS cleanup
├── docker-compose.yml
├── certs/ # Generated by setup.sh (gitignored)
├── docs/
│ ├── lab-guide.md
│ └── security-notes.md
├── vulnerable-app/
│ ├── Dockerfile
│ ├── package.json
│ ├── server.js
│ └── public/ ...
└── hardened-app/
├── Dockerfile
├── package.json
├── server.js
└── public/ ...
The SQLite database persists across Docker restarts (via named volumes). To start fresh:
From the UI: Click the “Reset Lab Database” button on either login page (bottom of the page). It asks for confirmation, then drops and recreates all tables.
From the terminal:
# Reset the vulnerable app
curl -X POST http://localhost:3001/api/reset-db # HTTP mode
curl -X POST https://cookiejar.test:3001/api/reset-db # HTTPS mode
# Reset the hardened app
curl -X POST http://localhost:3002/api/reset-db # HTTP mode
curl -X POST https://cookiejar.test:3002/api/reset-db # HTTPS mode
Nuclear option - delete the Docker volumes entirely:
docker compose down -v
docker compose up --build
If you used ./setup.sh to enable HTTPS, run ./cleanup.sh when you’re done:
./cleanup.sh
This removes the mkcert root CA from your system trust store and deletes the generated certificates. See docs/security-notes.md for more details on what mkcert does and why cleanup matters.
This project is for educational and authorized security testing purposes only.
This project is intended to help security professionals, students, and developers understand session security to build better defences.
A CyberDesserts project - Learn Cybersecurity By Doing