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

  1. Capture/verify the raw saslStart reply payload contains r=, s=, i= attributes
  2. Confirm the target is real mongod, not a mimicking service on 27017
  3. Rule out network devices stripping or rewriting the OP_MSG body
  4. 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

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


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