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

  1. Retry the handshake on a fresh connection to rule out a mixed-up response stream
  2. Verify no proxy is interleaving MongoDB protocol frames
  3. Confirm the client-first message sent actually contained an r= attribute
  4. 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

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


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/2925c3d5e413e4f9. Report an issue: GitHub.