projectdiscovery/nuclei · warning

packet length mismatch: header=%d body=%d

Error message

packet length mismatch: header=%d body=%d

What it means

Thrown while parsing a TDS pre-login response in nuclei' mssql JS library. The 16-bit big-endian packet length at bytes 2:4 of the reply must equal the total number of bytes buffered for the response; when they differ, the packet is self-inconsistent and cannot be trusted. In the current wiring (fingerprintMssql reads exactly packetLen bytes), the check is defensive and fires on replies from proxies/non-standard TDS peers or future multi-packet handling. When it escapes, FingerprintMssql wraps it as 'not a mssql service: ...' (fingerprint.go:147-149).

Source

Thrown at pkg/js/libs/mssql/fingerprint.go:191

	EncryptionMode  string
	Mars            bool
	InstanceMatches bool
}

func parsePreloginResponse(response []byte) (preloginData, error) {
	var out preloginData
	if len(response) < 8 {
		return out, fmt.Errorf("response too short for TDS header")
	}
	if response[0] != tdsTypeTabularResult {
		return out, fmt.Errorf("unexpected TDS type 0x%02x", response[0])
	}
	if response[1] != tdsStatusEOM {
		return out, fmt.Errorf("unexpected TDS status 0x%02x", response[1])
	}
	packetLength := int(binary.BigEndian.Uint16(response[2:4]))
	if len(response) != packetLength {
		return out, fmt.Errorf("packet length mismatch: header=%d body=%d", packetLength, len(response))
	}

	type optionToken struct {
		token  byte
		offset uint16
		length uint16
		data   []byte
	}

	position := 8
	var tokens []optionToken
	for position < len(response) {
		if response[position] == tdsTerminator {
			break
		}
		if position+5 > len(response) {
			return out, fmt.Errorf("truncated PL option token")
		}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Verify the port really speaks TDS: nmap -sV -p 1433 or mssql.IsMssql(host, port) — a false result means 'not MSSQL', not a bug
  2. If the target is genuinely SQL Server, capture the raw reply (info.Raw on success, or tcpdump) and compare the length field at offset 2:4 with the captured packet size
  3. Treat it as a fingerprint mismatch: rely on other service detection (banner, SSL, network probes) instead of the mssql fingerprint
  4. If writing Go code that calls parsePreloginResponse directly, ensure you buffer exactly the declared length before parsing
Defensive patterns

Strategy: try-catch

Validate before calling

const isMssql = mssql.IsMssql(host, port);
if (!isMssql) { log(host + ':' + port + ' is not mssql'); return; }

Try / catch

try {
  const info = mssql.FingerprintMssql(host, port);
} catch (e) {
  // parse failures surface as 'not a mssql service: <detail>'
  if (String(e).includes('not a mssql service')) { log('non-mssql target: ' + host); }
  else { throw e; }
}

Prevention

When it happens

Trigger: Calling mssql.FingerprintMssql(host, port) (or IsMssql, which maps it to false) against a TCP service whose reply's declared TDS length field disagrees with the bytes actually delivered — e.g. a load balancer, an RDP/other service on 1433, or a TDS server splitting the pre-login reply across packets.

Common situations: Scanning a host where port 1433 is actually fronted by a proxy or runs another protocol; hardened/patched SQL Server appliances with non-standard TDS framing; test harnesses feeding hand-crafted pre-login buffers.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/68774482dc79618c. Report an issue: GitHub.