Reflection Attack in an Authentication Protocol
Description
Reflection Attack in an Authentication Protocol is a vulnerability that occurs when simple authentication protocols using shared secret keys are susceptible to attacks where an attacker can use the target machine itself to generate valid responses to authentication challenges. In mutual authentication protocols that use the same pre-shared key across multiple entities, an attacker can exploit the protocol design by opening a second connection to the server and asking it to solve the challenge it posed in the first connection. When the server returns the solution, the attacker uses it in the original connection to authenticate without possessing the actual secret key.
Risk
Reflection attacks represent a fundamental weakness in poorly designed challenge-response authentication protocols. The risk is severe because attackers can authenticate without knowing the shared secret - they effectively trick the server into authenticating itself. These attacks are particularly dangerous in environments using symmetric key authentication where the same key is used for multiple purposes or by multiple parties. Systems using simple hash-based challenges without proper protocol design are highly susceptible. The attack requires only network access and the ability to open multiple connections, making it feasible for attackers with modest capabilities. Once successful, attackers gain full authenticated access, potentially compromising entire systems or networks.
Solution
Design authentication protocols to prevent reflection attacks through asymmetric challenge-response mechanisms. Use different keys for initiators and responders, ensuring the server cannot generate valid responses to its own challenges. Include role identifiers in challenge computations so responses from one role cannot be valid for another. Require the initiator to prove its identity before the responder sends any challenges. Implement challenge freshness by including timestamps, sequence numbers, or session identifiers. Use asymmetric cryptography where practical, as it inherently prevents reflection by using different keys for each party. Consider using established protocols like TLS with mutual authentication rather than designing custom protocols. Include connection-specific data in authentication computations to tie responses to specific sessions.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Successful reflection attacks allow attackers to authenticate as legitimate users without possessing the correct credentials, gaining full access to protected resources and capabilities. |
| Authentication, Non-Repudiation | Scope: Authentication, Non-Repudiation The authentication mechanism is completely bypassed, and actions performed by attackers may be attributed to the impersonated legitimate user, undermining accountability. |
Example Code
Vulnerable Code (Python)
The following examples demonstrate authentication vulnerable to reflection attacks:
# Vulnerable: Challenge-response protocol susceptible to reflection
import hashlib
import secrets
import socket
class VulnerableAuthServer:
def __init__(self, shared_secret):
self.shared_secret = shared_secret
self.pending_challenges = {}
def generate_challenge(self, connection_id):
# Generate challenge for client
challenge = secrets.token_hex(16)
self.pending_challenges[connection_id] = challenge
return challenge
def compute_response(self, challenge):
# Vulnerable: Same function used by both client and server
# Server will compute response if asked as a "client"
return hashlib.sha256(
(self.shared_secret + challenge).encode()
).hexdigest()
def verify_response(self, connection_id, response):
challenge = self.pending_challenges.get(connection_id)
if not challenge:
return False
expected = self.compute_response(challenge)
return response == expected
def handle_connection(self, conn, conn_id):
# Step 1: Send challenge to client
challenge = self.generate_challenge(conn_id)
conn.send(f"CHALLENGE:{challenge}".encode())
# Vulnerable: Will also respond to challenges sent TO it
data = conn.recv(1024).decode()
if data.startswith("RESPONSE:"):
# Verify client's response
response = data.split(":")[1]
if self.verify_response(conn_id, response):
conn.send(b"AUTH_SUCCESS")
else:
conn.send(b"AUTH_FAILED")
elif data.startswith("CHALLENGE:"):
# Vulnerable: Server responds to challenges!
# Attacker exploits this
incoming_challenge = data.split(":")[1]
response = self.compute_response(incoming_challenge)
conn.send(f"RESPONSE:{response}".encode())
# Attack scenario:
# 1. Attacker opens connection A
# 2. Server sends CHALLENGE:abc123 to attacker
# 3. Attacker opens connection B, sends CHALLENGE:abc123
# 4. Server responds with RESPONSE:<hash_of_abc123>
# 5. Attacker sends this response on connection A
# 6. Server authenticates attacker on connection A!
// Vulnerable: Java mutual authentication with reflection vulnerability
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class VulnerableAuthProtocol {
private final byte[] sharedSecret;
private final Map<String, String> pendingChallenges = new HashMap<>();
public VulnerableAuthProtocol(byte[] secret) {
this.sharedSecret = secret;
}
// Vulnerable: Single HMAC function used for all roles
public String computeHMAC(String challenge) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(sharedSecret, "HmacSHA256"));
byte[] result = mac.doFinal(challenge.getBytes());
return Base64.getEncoder().encodeToString(result);
} catch (Exception e) {
return null;
}
}
// Server generates challenge
public String serverChallenge(String sessionId) {
String challenge = UUID.randomUUID().toString();
pendingChallenges.put(sessionId, challenge);
return challenge;
}
// Vulnerable: Server also responds to challenges (for mutual auth)
public String respondToChallenge(String challenge) {
// Same function, same key - enables reflection
return computeHMAC(challenge);
}
public boolean verifyClientResponse(String sessionId, String response) {
String challenge = pendingChallenges.remove(sessionId);
if (challenge == null) return false;
String expected = computeHMAC(challenge);
return expected.equals(response);
}
}
Fixed Code (Python)
# Fixed: Protocol immune to reflection attacks
import hashlib
import hmac
import secrets
import time
class SecureAuthServer:
def __init__(self, shared_secret):
self.shared_secret = shared_secret.encode()
self.pending_challenges = {}
self.used_challenges = set()
def generate_challenge(self, connection_id):
challenge = secrets.token_hex(16)
timestamp = int(time.time())
self.pending_challenges[connection_id] = {
'challenge': challenge,
'timestamp': timestamp
}
return f"{challenge}:{timestamp}"
def compute_client_response(self, challenge, timestamp):
# Fixed: Include role identifier in computation
# Client uses "CLIENT" prefix, making server response invalid
message = f"CLIENT:{challenge}:{timestamp}"
return hmac.new(
self.shared_secret,
message.encode(),
hashlib.sha256
).hexdigest()
def compute_server_response(self, challenge, timestamp, client_nonce):
# Fixed: Different computation for server responses
# Includes client nonce to prevent reflection
message = f"SERVER:{challenge}:{timestamp}:{client_nonce}"
return hmac.new(
self.shared_secret,
message.encode(),
hashlib.sha256
).hexdigest()
def verify_response(self, connection_id, response, client_nonce):
pending = self.pending_challenges.get(connection_id)
if not pending:
return False
challenge = pending['challenge']
timestamp = pending['timestamp']
# Fixed: Check timestamp freshness
if abs(time.time() - timestamp) > 300: # 5 minute window
return False
# Fixed: Prevent challenge reuse
if challenge in self.used_challenges:
return False
expected = self.compute_client_response(challenge, timestamp)
if not hmac.compare_digest(response, expected):
return False
self.used_challenges.add(challenge)
del self.pending_challenges[connection_id]
return True
def handle_connection(self, conn, conn_id):
# Step 1: Receive client nonce first (client proves initiative)
data = conn.recv(1024).decode()
if not data.startswith("CLIENT_NONCE:"):
conn.send(b"PROTOCOL_ERROR")
return
client_nonce = data.split(":")[1]
# Step 2: Send challenge including client nonce
challenge_data = self.generate_challenge(conn_id)
conn.send(f"CHALLENGE:{challenge_data}".encode())
# Step 3: Receive and verify client response
data = conn.recv(1024).decode()
if data.startswith("RESPONSE:"):
response = data.split(":")[1]
if self.verify_response(conn_id, response, client_nonce):
# Send server's response for mutual auth
pending = self.pending_challenges.get(conn_id, {})
server_response = self.compute_server_response(
pending.get('challenge', ''),
pending.get('timestamp', 0),
client_nonce
)
conn.send(f"AUTH_SUCCESS:{server_response}".encode())
else:
conn.send(b"AUTH_FAILED")
// Fixed: Java authentication immune to reflection attacks
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
import java.security.SecureRandom;
public class SecureAuthProtocol {
private static final String CLIENT_ROLE = "CLIENT";
private static final String SERVER_ROLE = "SERVER";
private final byte[] sharedSecret;
private final Map<String, ChallengeData> pendingChallenges = new HashMap<>();
private final Set<String> usedChallenges = Collections.synchronizedSet(new HashSet<>());
public SecureAuthProtocol(byte[] secret) {
this.sharedSecret = secret;
}
// Fixed: Role-specific HMAC computation
private String computeRoleHMAC(String role, String challenge,
String timestamp, String nonce) {
try {
// Fixed: Include role in computation
String message = String.format("%s:%s:%s:%s",
role, challenge, timestamp, nonce);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(sharedSecret, "HmacSHA256"));
byte[] result = mac.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(result);
} catch (Exception e) {
return null;
}
}
public String computeClientResponse(String challenge,
String timestamp, String nonce) {
// Client role - cannot be used as server response
return computeRoleHMAC(CLIENT_ROLE, challenge, timestamp, nonce);
}
public String computeServerResponse(String challenge,
String timestamp, String clientNonce) {
// Server role with client's nonce - cannot be reflected
return computeRoleHMAC(SERVER_ROLE, challenge, timestamp, clientNonce);
}
public ChallengeData serverChallenge(String sessionId, String clientNonce) {
String challenge = UUID.randomUUID().toString();
String timestamp = String.valueOf(System.currentTimeMillis());
ChallengeData data = new ChallengeData(challenge, timestamp, clientNonce);
pendingChallenges.put(sessionId, data);
return data;
}
public boolean verifyClientResponse(String sessionId, String response) {
ChallengeData data = pendingChallenges.get(sessionId);
if (data == null) return false;
// Fixed: Prevent challenge reuse
if (usedChallenges.contains(data.challenge)) {
return false;
}
// Fixed: Verify timestamp freshness
long challengeTime = Long.parseLong(data.timestamp);
if (System.currentTimeMillis() - challengeTime > 300000) {
return false;
}
String expected = computeClientResponse(
data.challenge, data.timestamp, data.clientNonce);
if (expected.equals(response)) {
usedChallenges.add(data.challenge);
return true;
}
return false;
}
}
The fix uses role-specific computations and includes client-provided nonces to prevent reflection attacks.
Exploited in the Wild
SMB Relay Attacks (Windows Networks, Ongoing)
SMB relay attacks exploit reflection vulnerabilities in NTLM authentication, allowing attackers to relay authentication requests between servers and gain unauthorized access to network resources.
NTLM Reflection (Windows, 2008)
CVE-2008-4037 documented NTLM reflection vulnerabilities where Windows systems could be tricked into authenticating to themselves, allowing local privilege escalation.
Tools to Test/Exploit
-
Responder — LLMNR/NBT-NS/mDNS poisoner with built-in NTLM relay capabilities.
-
ntlmrelayx — Tool for NTLM relay attacks as part of Impacket suite.
-
Burp Suite — Web security tool for testing authentication protocol vulnerabilities.
CVE Examples
-
CVE-2005-3435 — Authentication via MD5 hash comparison vulnerable to replay and reflection.
-
CVE-2008-4037 — Windows NTLM reflection allowing local privilege escalation.
-
CVE-2019-1040 — Windows NTLM tampering vulnerability enabling relay attacks.
References
-
MITRE Corporation. "CWE-301: Reflection Attack in an Authentication Protocol." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/301.html
-
OWASP Foundation. "Authentication Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
-
Lowe, J. "A Taxonomy of DDoS Attacks and DDoS Defense Mechanisms." https://www.cs.virginia.edu/~cs757/papers/DDoS-taxonomy.pdf