shadow1ng/fscan · error
invalid SCRAM server-first payload
Error message
invalid SCRAM server-first payload
What it means
buildMongoSCRAMClientFinal parses the server-first SCRAM payload (r=...,s=...,i=...). If any of the nonce, salt, or iteration attributes are missing/empty, the payload is not a valid SCRAM server-first message and this error is returned.
Source
Thrown at plugins/services/mongodb.go:507
case 0x13: // decimal128
if pos+16 > docLen {
return reply, fmt.Errorf("short bson decimal128")
}
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)View on GitHub (pinned to 95cc12e753)
Solutions
- Capture/verify the raw saslStart reply payload contains r=, s=, i= attributes
- Confirm the target is real mongod, not a mimicking service on 27017
- Rule out network devices stripping or rewriting the OP_MSG body
- Check the payload for leading/trailing garbage that breaks attribute parsing
Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(`r=[^,]*,s=[^,]*,i=[^,]*`)
if !re.MatchString(serverFirst) {
return errors.New("server-first payload missing r/s/i attributes")
} Type guard
func looksLikeServerFirst(payload string) bool {
a := parseSCRAMAttributes(payload)
return a["r"] != "" && a["s"] != "" && a["i"] != ""
} Try / catch
final, err := buildMongoSCRAMClientFinal(u, p, cfb, serverFirst)
if err != nil {
if strings.Contains(err.Error(), "invalid SCRAM") {
log.Warnf("target sent non-SCRAM server-first payload: %v", err)
return
}
return err
} Prevention
- Validate the saslStart payload shape before SCRAM math
- Fingerprint the target as genuine mongod first
- Log raw payloads (hex) on failure to diagnose middlebox corruption
When it happens
Trigger: startReply.payload does not decode into 'r=nonce,s=salt,i=iterations' form — e.g. the server sent an error payload, a compressed/truncated payload, or an entirely different protocol message where parseSCRAMAttributes finds no r/s/i keys.
Common situations: Talking to a fake/honeypot MongoDB listener; a middlebox corrupting the payload; protocol-level mismatch after server upgrade (e.g. server replying with SCRAM-SHA-256-specific fields the parser ignores).
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
- invalid saslStart response
- invalid SCRAM salt: %w
- invalid SCRAM iteration count
- %s [socks5_unsupported_version]
- socks5_invalid_request
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/a951a16323a7d19e.
Report an issue: GitHub.