#!/usr/bin/env python3
"""
Authentication Attack Lab
The Hacker's Arsenal - Lesson 6
Practice authentication bypass techniques:
1. Basic login (no protection) - brute force target
2. Login with account lockout
3. Login with rate limiting (bypassable via X-Forwarded-For)
4. Login with weak CAPTCHA
5. Username enumeration
Run this script and open http://localhost:8888 in your browser.
WARNING: This code is INTENTIONALLY VULNERABLE for educational purposes.
"""
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import time
# Simulated user database
USERS = {
'admin': {'password': 'admin123', 'locked': False, 'attempts': 0},
'john': {'password': 'password1', 'locked': False, 'attempts': 0},
'jane': {'password': 'qwerty', 'locked': False, 'attempts': 0},
'bob': {'password': 'letmein', 'locked': False, 'attempts': 0},
'alice': {'password': 'Summer2024!', 'locked': False, 'attempts': 0},
}
# Rate limiting storage
REQUEST_LOG = {}
LOCKOUT_THRESHOLD = 5
LOCKOUT_DURATION = 30
HTML_TEMPLATE = '''<!DOCTYPE html>
<html>
<head>
<title>Auth Lab</title>
<style>
body {{ font-family: Arial; max-width: 800px; margin: 50px auto; background: #1a1a1a; color: #e0e0e0; }}
h1 {{ color: #6EFF24; }}
h2 {{ color: #6EFF24; font-size: 1.3em; }}
.section {{ background: #252525; padding: 20px; margin: 20px 0; border-radius: 8px; border-left: 3px solid #6EFF24; }}
input {{ padding: 10px; margin: 5px; background: #333; border: 1px solid #6EFF24; color: white; width: 200px; }}
button {{ background: #6EFF24; color: black; padding: 10px 20px; border: none; cursor: pointer; font-weight: bold; }}
.error {{ color: #ff4444; background: #331111; padding: 10px; border-radius: 5px; margin: 10px 0; }}
.success {{ color: #44ff44; background: #113311; padding: 10px; border-radius: 5px; margin: 10px 0; }}
.warning {{ color: #ffaa44; background: #332211; padding: 10px; border-radius: 5px; margin: 10px 0; }}
.hint {{ color: #888; font-size: 0.9em; margin-top: 10px; }}
code {{ background: #333; padding: 2px 6px; color: #6EFF24; }}
.info {{ background: #1a2a3a; padding: 10px; border-radius: 5px; margin: 10px 0; }}
</style>
</head>
<body>
<h1>Authentication Attack Lab</h1>
<div class="section">
<h2>1. Basic Login (No Protection)</h2>
<form action="/login1" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<button type="submit">Login</button>
</form>
{login1_result}
<p class="hint">Hint: Common usernames include admin, john, jane, bob, alice</p>
</div>
<div class="section">
<h2>2. Login with Lockout (5 attempts)</h2>
<form action="/login2" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<button type="submit">Login</button>
</form>
{login2_result}
<p class="hint">Hint: How can you test passwords without triggering lockout?</p>
</div>
<div class="section">
<h2>3. Login with Rate Limiting</h2>
<form action="/login3" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<button type="submit">Login</button>
</form>
{login3_result}
<p class="hint">Hint: Rate limiting is by IP. What if you could change your IP?</p>
</div>
<div class="section">
<h2>4. Login with CAPTCHA</h2>
<form action="/login4" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<input type="text" name="captcha" placeholder="What is 7 + 3?"><br>
<button type="submit">Login</button>
</form>
{login4_result}
<p class="hint">Hint: Is this CAPTCHA actually secure?</p>
</div>
<div class="section">
<h2>5. Username Enumeration</h2>
<form action="/check_user" method="GET">
<input type="text" name="username" placeholder="Check username"><br>
<button type="submit">Check Availability</button>
</form>
{enum_result}
<p class="hint">Hint: Watch for differences in responses between valid and invalid users</p>
</div>
<div class="section" style="border-left-color: #ff4444;">
<h2>Challenge Mode</h2>
<ol>
<li>Brute force the admin password using Burp Intruder</li>
<li>Bypass the account lockout to attack john's account</li>
<li>Enumerate all valid usernames</li>
<li>Find a way past the rate limiter</li>
</ol>
<div class="info">
<strong>Users:</strong> admin, john, jane, bob, alice<br>
<strong>Password hints:</strong> Common passwords, seasonal patterns
</div>
</div>
</body>
</html>'''
class AuthLabHandler(BaseHTTPRequestHandler):
def get_client_ip(self):
# VULNERABLE: Trusts X-Forwarded-For header
xff = self.headers.get('X-Forwarded-For')
if xff:
return xff.split(',')[0].strip()
return self.client_address[0]
def do_GET(self):
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
enum_result = ''
if parsed.path == '/check_user':
username = params.get('username', [''])[0]
# VULNERABLE: Different responses reveal valid usernames
if username in USERS:
enum_result = '<p class="warning">Username is taken</p>'
else:
enum_result = '<p class="info">Username is available</p>'
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
page = HTML_TEMPLATE.format(
login1_result='',
login2_result='',
login3_result='',
login4_result='',
enum_result=enum_result
)
self.wfile.write(page.encode())
def do_POST(self):
parsed = urlparse(self.path)
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length).decode()
params = parse_qs(post_data)
username = params.get('username', [''])[0]
password = params.get('password', [''])[0]
login1_result = ''
login2_result = ''
login3_result = ''
login4_result = ''
if parsed.path == '/login1':
# No protection - vulnerable to brute force
if username in USERS and USERS[username]['password'] == password:
login1_result = f'<p class="success">Welcome, {username}!</p>'
else:
login1_result = '<p class="error">Invalid credentials</p>'
elif parsed.path == '/login2':
# Account lockout after 5 attempts
if username in USERS:
user = USERS[username]
if user['locked']:
login2_result = '<p class="error">Account locked. Try again later.</p>'
elif user['password'] == password:
user['attempts'] = 0
login2_result = f'<p class="success">Welcome, {username}!</p>'
else:
user['attempts'] += 1
remaining = LOCKOUT_THRESHOLD - user['attempts']
if user['attempts'] >= LOCKOUT_THRESHOLD:
user['locked'] = True
login2_result = '<p class="error">Account locked after too many attempts!</p>'
else:
login2_result = f'<p class="error">Invalid password. {remaining} attempts remaining.</p>'
else:
# VULNERABLE: Different message for invalid username
login2_result = '<p class="error">User does not exist</p>'
elif parsed.path == '/login3':
# Rate limiting by IP (bypassable)
client_ip = self.get_client_ip()
current_time = time.time()
REQUEST_LOG[client_ip] = [t for t in REQUEST_LOG.get(client_ip, [])
if current_time - t < LOCKOUT_DURATION]
if len(REQUEST_LOG.get(client_ip, [])) >= LOCKOUT_THRESHOLD:
login3_result = '<p class="error">Too many requests. Please wait.</p>'
else:
REQUEST_LOG.setdefault(client_ip, []).append(current_time)
if username in USERS and USERS[username]['password'] == password:
login3_result = f'<p class="success">Welcome, {username}!</p>'
else:
login3_result = '<p class="error">Invalid credentials</p>'
elif parsed.path == '/login4':
# VULNERABLE: Static CAPTCHA
captcha = params.get('captcha', [''])[0]
if captcha != '10':
login4_result = '<p class="error">Invalid CAPTCHA</p>'
elif username in USERS and USERS[username]['password'] == password:
login4_result = f'<p class="success">Welcome, {username}!</p>'
else:
login4_result = '<p class="error">Invalid credentials</p>'
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
page = HTML_TEMPLATE.format(
login1_result=login1_result,
login2_result=login2_result,
login3_result=login3_result,
login4_result=login4_result,
enum_result=''
)
self.wfile.write(page.encode())
def log_message(self, format, *args):
print(f"[{self.get_client_ip()}] {args[0]}")
if __name__ == '__main__':
server = HTTPServer(('localhost', 8888), AuthLabHandler)
print("=" * 50)
print(" AUTHENTICATION LAB RUNNING")
print(" Open http://localhost:8888 in your browser")
print("=" * 50)
server.serve_forever()