VNX-301: Reflection Attack in an Authentication Protocol
Detects symmetric HMAC challenge-response code that answers a peer's challenge with exactly the computation it expects back, under the same key, so a challenge can be reflected to its sender and the answer replayed.
| Field | Value |
|---|---|
| Rule ID | VNX-301 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java, Python |
| CWE | CWE-301 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | reflection-attack challenge-response hmac cwe-301 |
| Source | internal/sast/rules/vnx-301.rego |
Overview
VNX-301 maps to CWE-301: Reflection Attack in an Authentication Protocol. In a mutual challenge-response protocol where both sides compute HMAC(K, challenge), an attacker who does not know K opens a second session to the victim, sends the victim’s own challenge as theirs, receives the victim’s answer, and hands it back in the first session. The victim has authenticated the attacker for them.
The rule reports, in a Python or Java file that both answers and verifies:
- an HMAC over a bare variable named for a challenge or nonce (Python
hmac.new(KEY, challenge, ...)/hmac.digest(KEY, challenge, ...); Javamac.doFinal(challenge)aftermac.init(new SecretKeySpec(KEY, ...))), - whose input came from the peer (a parameter of the enclosing function, or a value read with
recv/read/readFully/readLine/getParameter/requestin the 10 lines above), - whose result is returned, sent or written,
- where the same key is used for another HMAC in the file and the file compares MACs (
hmac.compare_digest,MessageDigest.isEqual,Arrays.equals).
What Gets Flagged
def answer_challenge(conn):
challenge = conn.recv(32)
conn.sendall(hmac.new(SHARED_KEY, challenge, hashlib.sha256).digest()) # FLAGGED
def authenticate_peer(conn):
nonce = os.urandom(32)
conn.sendall(nonce)
expected = hmac.new(SHARED_KEY, nonce, hashlib.sha256).digest()
return hmac.compare_digest(expected, conn.recv(32))
public byte[] respond(byte[] challenge) throws Exception {
mac.init(new SecretKeySpec(sharedKey, "HmacSHA256"));
return mac.doFinal(challenge); // FLAGGED
}
What Is Not Flagged
- A MAC that binds more than the challenge: an identity or role concatenated into the input in Python, a
mac.update(...)beforedoFinalin Java. - Different keys for each direction.
- A file that only verifies or only answers, the comparison itself, and comments.
Whether a bound label actually differs between the two directions is not checked: the label must name the responder (or direction), not be a constant both sides use.
Remediation
Bind the direction into every answer, for example HMAC(K, responder_id || challenge) where the verifier expects the peer’s identity, or derive a separate key per direction. Better still, use an established mutual-authentication protocol (TLS with client certificates, a PAKE, or a protocol with transcript binding).
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-301
// nosec vnx-301
Or record an ignore rule: vulnetix ignore add --rule vnx-301 --reason "...". See Suppressing findings.