{"record":{"id":"1f656526ed3e1c37","repo":"shadow1ng/fscan","slug":"mssql-truncated-error-token","errorCode":null,"errorMessage":"mssql: truncated error token","messagePattern":"mssql: truncated error token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/mssql_raw.go","lineNumber":335,"sourceCode":"\t\t\t}\n\t\t\tresult.sawLoginAck = true\n\t\t\tpos = next\n\t\tcase tdsTokenDone, tdsTokenDoneProc, tdsTokenDoneInProc:\n\t\t\tif pos+12 > len(payload) {\n\t\t\t\treturn false, fmt.Errorf(\"mssql: truncated done token\")\n\t\t\t}\n\t\t\tstatus := binary.LittleEndian.Uint16(payload[pos : pos+2])\n\t\t\treturn status&(tdsDoneError|tdsDoneSrvError) == 0, nil\n\t\tdefault:\n\t\t\treturn false, fmt.Errorf(\"mssql: unexpected login token 0x%02x\", token)\n\t\t}\n\t}\n\treturn false, nil\n}\n\nfunc mssqlParseErrorToken(payload []byte, pos int) (mssqlRawError, int, error) {\n\tif pos+2 > len(payload) {\n\t\treturn mssqlRawError{}, pos, fmt.Errorf(\"mssql: truncated error token\")\n\t}\n\tsize := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))\n\tend := pos + 2 + size\n\tif size < 6 || end > len(payload) || pos+8 > len(payload) {\n\t\treturn mssqlRawError{}, pos, fmt.Errorf(\"mssql: invalid error token size\")\n\t}\n\tpos += 2\n\tnumber := int32(binary.LittleEndian.Uint32(payload[pos : pos+4]))\n\tpos += 4\n\tpos += 2\n\tmessage, next, err := mssqlReadUSVarChar(payload, pos)\n\tif err != nil {\n\t\treturn mssqlRawError{}, pos, err\n\t}\n\treturn mssqlRawError{number: number, message: message}, end, mssqlEnsureSkipBVarStrings(payload, next, end)\n}\n\nfunc mssqlSkipUSVarError(payload []byte, pos int) (int, error) {","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/mssql_raw.go#L317-L353","documentation":"mssqlParseErrorToken was asked to read an ERROR token from a raw TDS login-response payload, but fewer than 2 bytes remained at the current offset, so the 2-byte token length header could not be read. This library parses the TDS wire protocol itself instead of using a driver, so it validates every field boundary and refuses to continue when the payload ends mid-token. It indicates a corrupt, truncated, or hostile server response rather than a caller mistake.","triggerScenarios":"Calling mssqlParseLoginTokens on a payload where the byte stream ends exactly at (or inside) the 2-byte length prefix of an ERROR token (0xAA), i.e. pos+2 > len(payload).","commonSituations":"A proxy, load balancer, or TLS terminator cut the server response short; a non-MSSQL service is answering on the SQL port and returns garbage; a fuzzer or malicious server sends a deliberately truncated packet; a custom packet-size/timeout dropped the tail of the login response.","solutions":["Retry the connection; a truncated response is usually transient network corruption.","Verify the host:port actually points at a Microsoft SQL Server (TDS) endpoint, not another service.","Capture the raw response (tcpdump/Wireshark) and check whether a middlebox is cutting the TCP stream.","Upgrade or patch the server if a known TDS bug emits malformed ERROR tokens."],"exampleFix":"// before: assume payload contains full tokens\nfor pos < len(payload) {\n    err, next, _ := mssqlParseErrorToken(payload, pos)\n    pos = next\n}\n// after: check remaining bytes for the 2-byte length header first\nfor pos+2 <= len(payload) {\n    err, next, perr := mssqlParseErrorToken(payload, pos)\n    if perr != nil {\n        return fmt.Errorf(\"malformed login response at offset %d: %w\", pos, perr)\n    }\n    pos = next\n}","handlingStrategy":"try-catch","validationCode":"if len(payload) < pos+2 {\n    return fmt.Errorf(\"payload too short for ERROR token header at offset %d\", pos)\n}","typeGuard":"func hasErrorTokenHeader(payload []byte, pos int) bool {\n    return pos+2 <= len(payload)\n}","tryCatchPattern":"err, next, perr := mssqlParseErrorToken(payload, pos)\nif perr != nil {\n    // close connection; do not reuse partial parse state\n    conn.Close()\n    return fmt.Errorf(\"malformed ERROR token at %d: %w\", pos, perr)\n}","preventionTips":["Always bound token parsing with len(payload) checks before every field read.","Retry connections once on truncation errors before surfacing to the user.","Use TLS end-to-end so middleboxes cannot alter or truncate the TDS stream.","Log hex dumps of failed payloads for post-mortem analysis."],"tags":["mssql","tds-protocol","truncated-payload","wire-parsing"],"backgroundTag":"unexpected-api-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"}