projectdiscovery/nuclei · warning
NTLM response not properly terminated with Sub-option End
Error message
NTLM response not properly terminated with Sub-option End
What it means
ParseNTLMResponse found the NTLMSSP signature but no 0xFF 0xF0 (IAC SE, Sub-option End) terminator after it. Nmap's original regex (NTLMSSP.*\xff\xf0) requires the terminator because various non-Microsoft telnet implementations advertise NTLM but emit malformed or unterminated data; this guard rejects exactly those.
Source
Thrown at pkg/utils/telnetmini/ntlm.go:37
Timestamp uint64 // Raw timestamp for skew calculation
}
// ParseNTLMResponse parses the NTLM response to extract system information
// This implements the exact parsing logic from the Nmap telnet-ntlm-info.nse script
func ParseNTLMResponse(data []byte) (*NTLMInfoResponse, error) {
// Continue only if NTLMSSP response is returned.
// Verify that the response is terminated with Sub-option End values as various
// non Microsoft telnet implementations support NTLM but do not return valid data.
// This matches the script's: local data = string.match(response, "(NTLMSSP.*)\xff\xf0")
ntlmStart := bytes.Index(data, []byte("NTLMSSP"))
if ntlmStart == -1 {
return nil, fmt.Errorf("NTLMSSP signature not found in response")
}
// Find the end of NTLM data (Sub-option End: 0xFF 0xF0)
ntlmEnd := bytes.Index(data[ntlmStart:], []byte{0xFF, 0xF0})
if ntlmEnd == -1 {
return nil, fmt.Errorf("NTLM response not properly terminated with Sub-option End")
}
// Extract NTLM data (NTLMSSP.*\xff\xf0)
ntlmData := data[ntlmStart : ntlmStart+ntlmEnd]
// Check message type (should be 2 for Challenge).
// The fixed header runs to offset 48 (target-info offset field ends at byte 48),
// so reject anything shorter before touching any field offsets.
if len(ntlmData) < 48 {
return nil, fmt.Errorf("NTLM response too short: need at least 48 bytes, got %d", len(ntlmData))
}
messageType := binary.LittleEndian.Uint32(ntlmData[8:12])
if messageType != 2 {
return nil, fmt.Errorf("expected NTLM challenge message, got type %d", messageType)
}
// Parse target name fieldsView on GitHub (pinned to 265b3a3dec)
Solutions
- Retry the probe — transient truncation over slow links often clears on a second attempt
- Increase the negotiation timeout (the Client/Probe accepts a timeout parameter; default is 7s total) when integrating the telnetmini package directly
- Treat as 'unsupported/malformed NTLM' and move on — do not fail the whole scan
- If persistent, capture the raw bytes (hexdump) to check whether a middlebox is rewriting IAC sequences
Defensive patterns
Strategy: validation
Validate before calling
// Verify terminator presence before invoking the parser:
start := bytes.Index(raw, []byte("NTLMSSP"))
if start == -1 { return nil, nil }
if bytes.Index(raw[start:], []byte{0xFF, 0xF0}) == -1 {
return nil, fmt.Errorf("truncated NTLM (no IAC SE) — consider retry or larger timeout")
}
info, err := telnetmini.ParseNTLMResponse(raw) Type guard
func ntlmTerminated(data []byte) bool {
i := bytes.Index(data, []byte("NTLMSSP"))
return i != -1 && bytes.Index(data[i:], []byte{0xFF, 0xF0}) != -1
} Try / catch
info, err := telnetmini.ParseNTLMResponse(resp)
if err != nil && strings.Contains(err.Error(), "not properly terminated") {
info, err = nil, nil // truncated: retry once with longer timeout, else skip
} Prevention
- Give telnet negotiation enough time on WAN links (raise the timeout parameter)
- Read the full sub-option payload before parsing; don't parse partial reads
- Expect non-compliant NTLM from embedded devices — treat as unsupported
When it happens
Trigger: A truncated telnet response — read deadline (1s per read in the negotiation loop) fired mid-NTLM payload; an IDS/IPS or firewall stripping sub-option bytes; a non-Windows implementation emitting NTLMSSP without proper SE framing; very slow servers whose NTLM blob arrives after the reader gave up.
Common situations: Scanning across WAN links with high latency (fixed 1-second read deadlines in telnet.go's negotiation loop are tight); middleboxes mangling telnet negotiation; embedded devices with half-baked NTLM implementations.
Related errors
- NTLMSSP signature not found in response
- NTLM response too short: need at least 48 bytes, got %d
- expected NTLM challenge message, got type %d
- waiting for login/username prompt: %w
- failed to send MS-TNAP login packet: %w
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/249f4d41ee620086.
Report an issue: GitHub.