Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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, ...); Java mac.doFinal(challenge) after mac.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/request in 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(...) before doFinal in 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.

Edit this pageSources: cli/internal/sast/rules/vnx-301.rego, cli/internal/sast/docs/vnx-301.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.