{"record":{"id":"68164c9d24f09c76","repo":"shadow1ng/fscan","slug":"mssql-packet-type-changed-in-message","errorCode":null,"errorMessage":"mssql: packet type changed in message","messagePattern":"mssql: packet type changed in message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/mssql_raw.go","lineNumber":433,"sourceCode":"\tif _, err := w.Write(header); err != nil {\n\t\treturn err\n\t}\n\t_, err := w.Write(payload)\n\treturn err\n}\n\nfunc mssqlReadMessage(r io.Reader) (byte, []byte, error) {\n\tvar packetType byte\n\tvar payload []byte\n\tfor {\n\t\theader := make([]byte, 8)\n\t\tif _, err := io.ReadFull(r, header); err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tif packetType == 0 {\n\t\t\tpacketType = header[0]\n\t\t} else if packetType != header[0] {\n\t\t\treturn 0, nil, fmt.Errorf(\"mssql: packet type changed in message\")\n\t\t}\n\t\tsize := int(binary.BigEndian.Uint16(header[2:4]))\n\t\tif size < 8 {\n\t\t\treturn 0, nil, fmt.Errorf(\"mssql: invalid packet size\")\n\t\t}\n\t\tchunk := make([]byte, size-8)\n\t\tif _, err := io.ReadFull(r, chunk); err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tif len(payload)+len(chunk) > maxTDSMessageSize {\n\t\t\treturn 0, nil, fmt.Errorf(\"mssql: message too large\")\n\t\t}\n\t\tpayload = append(payload, chunk...)\n\t\tif header[1]&tdsStatusEOM != 0 {\n\t\t\treturn packetType, payload, nil\n\t\t}\n\t}\n}","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/mssql_raw.go#L415-L451","documentation":"mssqlReadMessage assembles a TDS message from one or more packets; every packet of a message must carry the same packet type byte. If a continuation packet's type differs from the type recorded for the first packet, the stream violates the TDS framing rules and reading aborts with this error.","triggerScenarios":"Reading a prelogin or login response where a multi-packet message (status EOM bit not set on the first packet) arrives whose subsequent packet header contains a different type byte than the first — a corrupted, interleaved, or non-TDS stream.","commonSituations":"Port serving a different protocol that happens to emit 8-byte-aligned garbage; a proxy interleaving responses from two connections; an intentional malicious server mixing packet types to break parsers.","solutions":["Confirm the peer is a real SQL Server speaking TDS on the expected port.","Discard the connection and restart the handshake — the stream is desynchronized once framing is violated.","Check for middleboxes/proxies multiplexing the TCP stream and connect directly.","If parsing crafted input, note the parser correctly rejects it; treat it as untrusted and stop."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"pktType, payload, err := mssqlReadMessage(conn)\nif err != nil {\n    if strings.Contains(err.Error(), \"packet type changed\") {\n        conn.Close() // stream desynchronized; restart handshake or drop target\n    }\n}","preventionTips":["Connect only to confirmed SQL Server TDS endpoints.","Avoid proxies that multiplex or reorder TCP streams.","Log the offending packet type bytes to identify non-TDS peers.","Treat framing violations as fatal for the connection."],"tags":["mssql","tds","protocol-framing","malformed-input"],"backgroundTag":"unexpected-response-shape","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"}