shadow1ng/fscan · error

invalid saslStart response

Error message

invalid saslStart response

What it means

doMongoDBAuth performs a SCRAM-SHA-1 handshake against MongoDB. After sending saslStart, the server reply must set the conversation id and carry a non-empty payload containing the server-first message. If either is missing, the plugin rejects the reply as an 'invalid saslStart response' because the SCRAM challenge cannot proceed without it.

Source

Thrown at plugins/services/mongodb.go:145

		state.IncrementTCPFailedPacketCount()
		return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
	}
	resp, err = readMongoMsg(conn, timeout)
	if err != nil {
		state.IncrementTCPFailedPacketCount()
		return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
	}

	startReply, err := parseMongoCommandReply(resp)
	if err != nil {
		state.IncrementTCPFailedPacketCount()
		return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
	}
	if !startReply.ok {
		return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: fmt.Errorf("authentication failed: %s", startReply.errmsg)}
	}
	if !startReply.conversationSet || len(startReply.payload) == 0 {
		return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: fmt.Errorf("invalid saslStart response")}
	}

	serverFirst := string(startReply.payload)
	clientFinal, err := buildMongoSCRAMClientFinal(cred.Username, cred.Password, clientFirstBare, serverFirst)
	if err != nil {
		return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: err}
	}

	saslContinueCmd := buildMongoCommand("admin", orderedDoc(
		kv("saslContinue", 1),
		kv("conversationId", int(startReply.conversationID)),
		kv("payload", []byte(clientFinal)),
	))
	if _, err := sendMongoMsg(ctx, conn, saslContinueCmd, timeout); err != nil {
		state.IncrementTCPFailedPacketCount()
		return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
	}
	resp, err = readMongoMsg(conn, timeout)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the target is a genuine MongoDB server by checking the hello/ismaster handshake response before attempting auth
  2. Bypass or remove intermediate proxies/load balancers that alter OP_MSG payloads
  3. Confirm the server supports SCRAM (authenticationMechanisms in getCmdLineOpts); if only MONGODB-CR/x509 is enabled use the appropriate plugin path
  4. Re-run the scan directly against the host to rule out transient packet corruption

Example fix

// before
if !startReply.conversationSet || len(startReply.payload) == 0 {
    return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: fmt.Errorf("invalid saslStart response")}
}
// after
if !startReply.conversationSet || len(startReply.payload) == 0 {
    return &AuthResult{Success: false, ErrorType: ErrorTypeAuth,
        Error: fmt.Errorf("invalid saslStart response (conversationSet=%v payloadLen=%d)", startReply.conversationSet, len(startReply.payload))}
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify target speaks MongoDB before auth
if err := probeHello(ctx, addr); err != nil {
    return fmt.Errorf("not a MongoDB endpoint: %w", err)
}

Type guard

func validSaslStart(reply mongoReply) bool {
    return reply.ok && reply.conversationSet && len(reply.payload) > 0
}

Try / catch

res, err := doMongoDBAuth(ctx, addr, cred)
if err != nil {
    var authErr *AuthResult
    if errors.As(err, &authErr) && authErr.ErrorType == ErrorTypeAuth {
        log.Warn("saslStart reply malformed; target may not be SCRAM-capable")
        return
    }
    return err
}

Prevention

When it happens

Trigger: The MongoDB server responds to the saslStart OpMsg command with ok=true but omits the 'conversationId' field or returns an empty 'payload' byte string — e.g. a proxy/middleware strips fields, or the server does not actually speak SCRAM.

Common situations: Scanning a mongod behind a load balancer or API gateway that mangles OP_MSG replies; targeting a non-MongoDB service on port 27017 that mimics minimal BSON responses; MongoDB versions/configurations with authentication mechanisms restricted so SCRAM negotiation behaves unexpectedly.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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