projectdiscovery/nuclei · warning

NTLM response too short: need at least 48 bytes, got %d

Error message

NTLM response too short: need at least 48 bytes, got %d

What it means

After locating NTLMSSP and the 0xFF 0xF0 terminator, ParseNTLMResponse enforces a 48-byte minimum: the fixed NTLM Challenge header (through the target-info offset field ending at byte 48) must be intact before any field offsets are read. Shorter blobs return 'NTLM response too short: need at least 48 bytes, got %d' — a hardening check to prevent out-of-range slices and panics on garbage data.

Source

Thrown at pkg/utils/telnetmini/ntlm.go:47

	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 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) {

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Treat as malformed/unsupported NTLM and continue the scan — the data carries no parseable info
  2. Retry once to rule out transient truncation from read deadlines
  3. Raise the timeout if integrating telnetmini directly and targeting high-latency networks
  4. If it reproduces deterministically, hexdump the payload — likely a decoy or non-compliant device worth noting in findings
Defensive patterns

Strategy: validation

Validate before calling

// Enforce the same 48-byte floor before parsing:
start := bytes.Index(raw, []byte("NTLMSSP"))
end := bytes.Index(raw[start:], []byte{0xFF, 0xF0})
if end < 48 {
    return nil, fmt.Errorf("NTLM blob too short (%d bytes) — malformed or decoy", end)
}
// safe to call ParseNTLMResponse

Type guard

func ntlmLongEnough(data []byte) bool {
    i := bytes.Index(data, []byte("NTLMSSP"))
    if i == -1 { return false }
    j := bytes.Index(data[i:], []byte{0xFF, 0xF0})
    return j >= 48
}

Try / catch

if err != nil && strings.Contains(err.Error(), "too short") {
    // malformed/decoy payload — record host as 'NTLM malformed' and move on
    return nil
}

Prevention

When it happens

Trigger: An NTLMSSP signature followed by a terminator but with fewer than 48 bytes between them: heavily truncated payloads, decoy/honeypot banners containing the literal string 'NTLMSSP', or non-standard implementations emitting abbreviated messages. The distance between signature and terminator is exactly what is measured.

Common situations: Honeypots and tarpits that echo keywords to elicit further probes; aggressive scanners pasting the NTLMSSP magic without a real message; responses cut by the 1-second read deadline mid-payload.

Related errors


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