shadow1ng/fscan · error

invalid SCRAM salt: %w

Error message

invalid SCRAM salt: %w

What it means

Guard in buildMongoSCRAMClientFinal: the base64-decoded SCRAM salt from the MongoDB server-first message could not be used (decode failure or unusable length). The SCRAM-SHA-1 exchange cannot continue because ClientKey computation requires the salt and iteration count.

Source

Thrown at plugins/services/mongodb.go:515

	}
	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))
	for i := range clientKey {
		proof[i] = clientKey[i] ^ clientSignature[i]
	}
	return clientFinalWithoutProof + ",p=" + base64.StdEncoding.EncodeToString(proof), nil

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Inspect the s= value for non-standard base64 characters or bad padding
  2. Support RawStdEncoding/URL-safe fallback decoding if a non-standard server is expected
  3. Capture the raw reply to check for truncation en route
  4. Confirm against a known-good mongod that the payload decodes cleanly

Example fix

// before
salt, err := base64.StdEncoding.DecodeString(saltB64)
// after
salt, err := base64.StdEncoding.DecodeString(saltB64)
if err != nil {
    salt, err = base64.RawStdEncoding.DecodeString(strings.TrimRight(saltB64, "="))
}
Defensive patterns

Strategy: validation

Validate before calling

if _, err := base64.StdEncoding.DecodeString(saltB64); err != nil {
    return fmt.Errorf("salt not standard base64: %w", err)
}

Type guard

func isValidB64(s string) bool {
    _, err := base64.StdEncoding.DecodeString(s)
    return err == nil && len(s) > 0
}

Try / catch

final, err := buildMongoSCRAMClientFinal(u, p, cfb, serverFirst)
if err != nil {
    var b64Err base64.CorruptInputError
    if errors.As(err, &b64Err) {
        log.Warnf("salt base64 corrupt at offset %d", int(b64Err))
        return
    }
    return err
}

Prevention

When it happens

Trigger: The s= attribute in the server-first payload contains characters outside the standard base64 alphabet or has wrong padding — e.g. URL-safe base64 (-/ instead of +/), whitespace, or truncation by a middlebox.

Common situations: Proxies rewriting payloads; custom server implementations that URL-safe-encode the salt; corrupted scan traffic over unreliable links.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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