{"record":{"id":"3becd08a5abbe8d7","repo":"shadow1ng/fscan","slug":"mssql-empty-prelogin-response","errorCode":null,"errorMessage":"mssql: empty prelogin response","messagePattern":"mssql: empty prelogin response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/mssql_raw.go","lineNumber":157,"sourceCode":"\t}\n\tpayload.WriteByte(tdsPreloginTerminator)\n\tfor _, key := range keys {\n\t\tpayload.Write(fields[byte(key)])\n\t}\n\n\treturn mssqlWritePacket(w, tdsPacketPrelogin, payload.Bytes())\n}\n\nfunc mssqlReadPrelogin(r io.Reader) error {\n\tpacketType, payload, err := mssqlReadMessage(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif packetType != tdsPacketReply {\n\t\treturn fmt.Errorf(\"mssql: invalid prelogin response packet type %d\", packetType)\n\t}\n\tif len(payload) == 0 {\n\t\treturn fmt.Errorf(\"mssql: empty prelogin response\")\n\t}\n\n\tfields, err := mssqlParsePreloginFields(payload)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, ok := fields[tdsPreloginEncryption]; !ok {\n\t\treturn fmt.Errorf(\"mssql: prelogin response missing encryption field\")\n\t}\n\treturn nil\n}\n\nfunc mssqlParsePreloginFields(payload []byte) (map[byte][]byte, error) {\n\tfields := make(map[byte][]byte)\n\tfor pos := 0; ; pos += 5 {\n\t\tif pos >= len(payload) {\n\t\t\treturn nil, fmt.Errorf(\"mssql: invalid prelogin option table\")\n\t\t}","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/mssql_raw.go#L139-L175","documentation":"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.","triggerScenarios":"mssqlReadPrelogin reads packet type 4 with an empty payload after sending the prelogin packet (payload length exactly 8 in the TDS header).","commonSituations":"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.","solutions":["Verify the target is a genuine SQL Server endpoint (try sqlcmd or go-mssqldb against the same host:port).","Check firewall/IDS devices that may truncate packets.","Retry the connection; transient mid-handshake resets produce this intermittently.","Inspect network logs for connection resets from the server."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Preflight reachability so empty responses are not confused with unreachable hosts:\nconn, err := net.DialTimeout(\"tcp\", net.JoinHostPort(host, fmt.Sprint(port)), timeout)\nif err != nil { return fmt.Errorf(\"unreachable: %w\", err) }\n_ = conn.Close()","typeGuard":null,"tryCatchPattern":"var res *mssqlRawResult\nvar err error\nfor i := 0; i < 2; i++ {\n    res, err = mssqlRawLogin(ctx, host, port, user, pass, timeout)\n    if err == nil || !strings.Contains(err.Error(), \"empty prelogin response\") {\n        break\n    }\n    time.Sleep(200 * time.Millisecond)\n}","preventionTips":["Retry once on empty responses — mid-handshake resets are often transient.","Check firewall/IDS devices that may strip TCP payloads.","Validate the endpoint with sqlcmd before writing raw probes."],"tags":["mssql","tds","prelogin","empty-response"],"backgroundTag":"empty-response-body","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}