shadow1ng/fscan · error
invalid SCRAM nonce
Error message
invalid SCRAM nonce
What it means
During SCRAM the server's nonce (r=) must begin with the client nonce the plugin generated in its client-first message. If the client nonce is empty or the server nonce is not prefixed by it, this error is thrown — the reply is either from a different handshake or is malicious/spoofed.
Source
Thrown at plugins/services/mongodb.go:511
pos += 16
default:
return reply, fmt.Errorf("unsupported bson type 0x%02x for key %s", typ, key)
}
}
return reply, nil
}
func buildMongoSCRAMClientFinal(username, password, clientFirstBare, serverFirst string) (string, error) {
attrs := parseSCRAMAttributes(serverFirst)
serverNonce := attrs["r"]
saltB64 := attrs["s"]
iterText := attrs["i"]
if serverNonce == "" || saltB64 == "" || iterText == "" {
return "", fmt.Errorf("invalid SCRAM server-first payload")
}
clientNonce := scramAttr(clientFirstBare, "r")
if clientNonce == "" || !strings.HasPrefix(serverNonce, clientNonce) {
return "", fmt.Errorf("invalid SCRAM nonce")
}
salt, err := base64.StdEncoding.DecodeString(saltB64)
if err != nil {
return "", fmt.Errorf("invalid SCRAM salt: %w", err)
}
iterations, err := strconv.Atoi(iterText)
if err != nil || iterations <= 0 {
return "", fmt.Errorf("invalid SCRAM iteration count")
}
clientFinalWithoutProof := "c=biws,r=" + serverNonce
authMessage := clientFirstBare + "," + serverFirst + "," + clientFinalWithoutProof
digest := md5.Sum([]byte(username + ":mongo:" + password))
saltedPassword := pbkdf2.Key([]byte(fmt.Sprintf("%x", digest)), salt, iterations, sha1.Size, sha1.New)
clientKey := mongoHMAC(saltedPassword, []byte("Client Key"))
storedKey := sha1.Sum(clientKey)
clientSignature := mongoHMAC(storedKey[:], []byte(authMessage))
proof := make([]byte, len(clientKey))View on GitHub (pinned to 95cc12e753)
Solutions
- Retry the handshake on a fresh connection to rule out a mixed-up response stream
- Verify no proxy is interleaving MongoDB protocol frames
- Confirm the client-first message sent actually contained an r= attribute
- Treat repeated occurrences as evidence the target is not a legitimate SCRAM-speaking MongoDB
Defensive patterns
Strategy: validation
Validate before calling
clientNonce := scramAttr(clientFirstBare, "r")
if clientNonce == "" || !strings.HasPrefix(attrs["r"], clientNonce) {
return errors.New("server nonce does not extend client nonce")
} Type guard
func nonceExtendsClient(serverFirst, clientFirstBare string) bool {
return strings.HasPrefix(scramAttr(serverFirst, "r"), scramAttr(clientFirstBare, "r"))
} Try / catch
if err := doMongoDBAuth(ctx, addr, cred); err != nil && strings.Contains(err.Error(), "invalid SCRAM nonce") {
log.Warn("possible spoofed/replayed challenge; retrying on fresh connection")
return retryFreshConn(ctx, addr, cred)
} Prevention
- Always run SCRAM on a dedicated single-use connection
- Treat nonce mismatch as a security signal, not just a parse failure
- Verify no TLS-terminating proxy re-injects MongoDB frames
When it happens
Trigger: serverFirst's r attribute does not start with the client-first bare message's r value: replies out of order, a replayed/foreign challenge, or a server that echoes a fresh nonce without incorporating the client's.
Common situations: MITM or spoofed responses during a scan; multiplexed connections where a reply from another session is read; a deliberately deceptive target service.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- authentication failed: %s
- invalid saslStart response
- invalid SCRAM server-first payload
- invalid SCRAM salt: %w
- invalid SCRAM iteration count
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/2925c3d5e413e4f9.
Report an issue: GitHub.