shadow1ng/fscan · error

mssql: empty prelogin response

Error message

mssql: empty prelogin response

What it means

The prelogin response arrived as a valid REPLY packet but contained zero payload bytes. A correct prelogin response must include an option table plus values; an empty body means the server sent a degenerate response.

Source

Thrown at plugins/services/mssql_raw.go:157

	}
	payload.WriteByte(tdsPreloginTerminator)
	for _, key := range keys {
		payload.Write(fields[byte(key)])
	}

	return mssqlWritePacket(w, tdsPacketPrelogin, payload.Bytes())
}

func mssqlReadPrelogin(r io.Reader) error {
	packetType, payload, err := mssqlReadMessage(r)
	if err != nil {
		return err
	}
	if packetType != tdsPacketReply {
		return fmt.Errorf("mssql: invalid prelogin response packet type %d", packetType)
	}
	if len(payload) == 0 {
		return fmt.Errorf("mssql: empty prelogin response")
	}

	fields, err := mssqlParsePreloginFields(payload)
	if err != nil {
		return err
	}
	if _, ok := fields[tdsPreloginEncryption]; !ok {
		return fmt.Errorf("mssql: prelogin response missing encryption field")
	}
	return nil
}

func mssqlParsePreloginFields(payload []byte) (map[byte][]byte, error) {
	fields := make(map[byte][]byte)
	for pos := 0; ; pos += 5 {
		if pos >= len(payload) {
			return nil, fmt.Errorf("mssql: invalid prelogin option table")
		}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the target is a genuine SQL Server endpoint (try sqlcmd or go-mssqldb against the same host:port).
  2. Check firewall/IDS devices that may truncate packets.
  3. Retry the connection; transient mid-handshake resets produce this intermittently.
  4. Inspect network logs for connection resets from the server.
Defensive patterns

Strategy: retry

Validate before calling

// Preflight reachability so empty responses are not confused with unreachable hosts:
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, fmt.Sprint(port)), timeout)
if err != nil { return fmt.Errorf("unreachable: %w", err) }
_ = conn.Close()

Try / catch

var res *mssqlRawResult
var err error
for i := 0; i < 2; i++ {
    res, err = mssqlRawLogin(ctx, host, port, user, pass, timeout)
    if err == nil || !strings.Contains(err.Error(), "empty prelogin response") {
        break
    }
    time.Sleep(200 * time.Millisecond)
}

Prevention

When it happens

Trigger: mssqlReadPrelogin reads packet type 4 with an empty payload after sending the prelogin packet (payload length exactly 8 in the TDS header).

Common situations: Server-side connection teardown mid-handshake; middleware or firewall stripping the body; a honeypot or non-MSSQL service on the port returning an empty TDS-shaped packet.

Related errors


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