projectdiscovery/nuclei · error
ntlm: message too short
Error message
ntlm: message too short
What it means
parseNTLMMessage rejects any decoded blob shorter than 12 bytes, the minimum NTLMSSP fixed header: the 8-byte 'NTLMSSP\x00' signature plus the 4-byte little-endian message type. The input base64-decoded successfully but the result is too small to be any valid NTLM message, so offsets like data[8:12] would be out of bounds.
Source
Thrown at pkg/js/libs/http/ntlm.go:90
case strings.HasPrefix(lower, "ntlm "):
s = strings.TrimSpace(s[5:])
case strings.HasPrefix(lower, "negotiate "):
s = strings.TrimSpace(s[10:])
}
// Some servers return "Negotiate <spnego>" - still try base64 of remainder.
raw, err := base64.StdEncoding.DecodeString(s)
if err != nil {
raw, err = base64.RawStdEncoding.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("ntlm: base64 decode: %w", err)
}
}
return raw, nil
}
func parseNTLMMessage(data []byte) (*NTLMInfo, error) {
if len(data) < 12 {
return nil, fmt.Errorf("ntlm: message too short")
}
if !bytes.HasPrefix(data, []byte("NTLMSSP\x00")) {
return nil, fmt.Errorf("ntlm: missing NTLMSSP signature")
}
msgType := binary.LittleEndian.Uint32(data[8:12])
info := &NTLMInfo{MessageType: int(msgType)}
if msgType != 2 {
// Type 1/3: return type only; TargetInfo is Type-2 specific.
return info, nil
}
if len(data) < 48 {
return nil, fmt.Errorf("ntlm: challenge too short")
}
targetNameLen := binary.LittleEndian.Uint16(data[12:14])
targetNameOffset := binary.LittleEndian.Uint32(data[16:20])
if targetNameLen > 0 && int(targetNameOffset)+int(targetNameLen) <= len(data) {
info.TargetName = decodeUTF16LE(data[targetNameOffset : targetNameOffset+uint32(targetNameLen)])View on GitHub (pinned to 265b3a3dec)
Solutions
- Re-fetch the challenge and pass the complete token from the 401 response that follows a Type-1 negotiate (http.NegotiateNTLM())
- Check the base64 token length: 16 base64 characters are needed to represent the 12-byte minimum, so shorter tokens can never be valid
- If the endpoint is not NTLM-capable, skip the decode step entirely
Defensive patterns
Strategy: validation
Validate before calling
function isPlausibleNTLMLength(token) {
// 12-byte minimum NTLMSSP header needs at least 16 base64 characters
return /^[A-Za-z0-9+/]+={0,2}$/.test(token) && token.length >= 16;
} Try / catch
try {
const info = http.DecodeNTLM(token);
} catch (e) {
// Blob too short to be NTLM; ignore endpoint
} Prevention
- Always decode the untouched challenge from the server response, never a hand-copied fragment
- Prefer obtaining the challenge by sending a Type-1 negotiate so the token is complete
When it happens
Trigger: DecodeNTLM('NTLM QQ==') (decodes to a single byte); a challenge token truncated mid-base64 that still decodes to a few bytes; passing base64 of short unrelated strings; proxy or middleware trimming the header value.
Common situations: Copy-pasting partial tokens from documentation or Burp; test fixtures with placeholder tokens like 'AAAA'; responses passed through gateways that shorten header values; probing hosts that are not AD members.
Related errors
- ntlm: empty blob
- ntlm: challenge too short
- failed to parse NTLM response: %w
- expected NTLM challenge message, got type %d
- validation failed for these fields
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/060422a6cfe09ca7.
Report an issue: GitHub.