projectdiscovery/nuclei · warning
expected NTLM challenge message, got type %d
Error message
expected NTLM challenge message, got type %d
What it means
ParseNTLMResponse reads the NTLM message type at offset 8 and requires 2 (NEGOTIATE-to-CHALLENGE exchange: the server's Challenge message is what carries target name, timestamps, and NetBIOS info this parser extracts). Type 1 (Negotiate), type 3 (Authenticate), or garbage yields 'expected NTLM challenge message, got type %d'.
Source
Thrown at pkg/utils/telnetmini/ntlm.go:52
// 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 fields
targetNameLen := binary.LittleEndian.Uint16(ntlmData[12:14])
targetNameOffset := binary.LittleEndian.Uint32(ntlmData[16:20])
// Parse target info fields
targetInfoLen := binary.LittleEndian.Uint16(ntlmData[40:42])
targetInfoOffset := binary.LittleEndian.Uint32(ntlmData[44:48])
// Extract target name (Target Name will always be returned under any implementation)
var targetName string
if targetNameLen > 0 && int(targetNameOffset) < len(ntlmData) {
end := int(targetNameOffset) + int(targetNameLen)
if end <= len(ntlmData) {
targetName = string(ntlmData[targetNameOffset:end])
}
}View on GitHub (pinned to 265b3a3dec)
Solutions
- Treat as unsupported — the parser only exists to harvest Challenge-message info, other types carry none of it
- If the reported type is 1, the server likely echoed your negotiation — one retry occasionally gets the real Challenge
- Verify the target really is Windows telnet (banner/other probes) before investing further
- Hexdump the blob when integrating telnetmini yourself, to confirm which message type the device emits
Defensive patterns
Strategy: validation
Validate before calling
// Peek at the message type (offset 8, LE uint32) before full parsing:
start := bytes.Index(raw, []byte("NTLMSSP"))
if start >= 0 && len(raw) >= start+12 {
if mt := binary.LittleEndian.Uint32(raw[start+8 : start+12]); mt != 2 {
return nil, fmt.Errorf("not a challenge message (type %d) — nothing to extract", mt)
}
} Type guard
func isNTLMChallenge(data []byte) bool {
i := bytes.Index(data, []byte("NTLMSSP"))
return i >= 0 && len(data) >= i+12 && binary.LittleEndian.Uint32(data[i+8:i+12]) == 2
} Try / catch
if err != nil && strings.Contains(err.Error(), "expected NTLM challenge") {
return nil, nil // wrong message type — no info to harvest, skip host
} Prevention
- Only the Challenge (type 2) message carries extractable target info — validate type first
- One retry can resolve servers that echo the type-1 Negotiate
- Confirm the target is Windows telnet before relying on NTLM extraction
When it happens
Trigger: The captured sub-option contains a different NTLM message than the challenge: e.g. a server that echoes the client's type-1 Negotiate back, a capture that picked up a type-3 exchange, or 4+ bytes of non-message data that happens to follow the signature. Type values other than 2 (commonly 1 or 3) appear in the message.
Common situations: Non-compliant telnet implementations that mirror negotiation; security appliances that respond with odd NTLM blobs; off-by-one conditions where the signature match lands on embedded NTLM bytes inside other data (e.g. SMB-over-telnet tunnels).
Related errors
- failed to parse NTLM response: %w
- NTLMSSP signature not found in response
- NTLM response not properly terminated with Sub-option End
- NTLM response too short: need at least 48 bytes, got %d
- ntlm: message too short
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/4026dcb4bc70c4dd.
Report an issue: GitHub.