{"record":{"id":"3821f17209da0016","repo":"projectdiscovery/nuclei","slug":"ntlm-empty-blob","errorCode":null,"errorMessage":"ntlm: empty blob","messagePattern":"ntlm: empty blob","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/js/libs/http/ntlm.go","lineNumber":68,"sourceCode":"}\n\n// NegotiateNTLM returns a base64 Type-1 NTLM negotiate message suitable for\n// an Authorization header (without the \"NTLM \" prefix).\n// @example\n// ```javascript\n// const http = require('nuclei/http');\n// const client = new http.Client();\n// client.SetHeader('Authorization', 'NTLM ' + http.NegotiateNTLM());\n// const resp = client.Get('https://exchange.acme.local/ews/');\n// ```\nfunc NegotiateNTLM() string {\n\treturn base64.StdEncoding.EncodeToString(createNegotiateMessage())\n}\n\nfunc decodeNTLMBlob(blob string) ([]byte, error) {\n\ts := strings.TrimSpace(blob)\n\tif s == \"\" {\n\t\treturn nil, fmt.Errorf(\"ntlm: empty blob\")\n\t}\n\tlower := strings.ToLower(s)\n\tswitch {\n\tcase strings.HasPrefix(lower, \"ntlm \"):\n\t\ts = strings.TrimSpace(s[5:])\n\tcase strings.HasPrefix(lower, \"negotiate \"):\n\t\ts = strings.TrimSpace(s[10:])\n\t}\n\t// Some servers return \"Negotiate <spnego>\" - still try base64 of remainder.\n\traw, err := base64.StdEncoding.DecodeString(s)\n\tif err != nil {\n\t\traw, err = base64.RawStdEncoding.DecodeString(s)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ntlm: base64 decode: %w\", err)\n\t\t}\n\t}\n\treturn raw, nil\n}","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/http/ntlm.go#L50-L86","documentation":"DecodeNTLM was called with an empty or whitespace-only blob. The function expects the WWW-Authenticate/Authorization header value (with optional 'NTLM '/'Negotiate ' prefix) or raw base64 NTLMSSP data; empty input usually means the response carried no WWW-Authenticate header, i.e. the target did not offer an NTLM challenge at all.","triggerScenarios":"const info = http.DecodeNTLM(resp.GetHeader('WWW-Authenticate')) when the response has no WWW-Authenticate header (GetHeader returns ''); passing a null/undefined/unset template variable; probing a non-NTLM endpoint that answers 200 without a challenge.","commonSituations":"NTLM fingerprint templates run against arbitrary HTTP services; checking the wrong response in a multi-step flow; servers that only emit the challenge on a 401 that the request never triggered (no NTLM negotiate sent first).","solutions":["Guard before decoding: only call DecodeNTLM when the header is non-empty","Trigger the challenge first: client.SetHeader('Authorization', 'NTLM ' + http.NegotiateNTLM()) so the 401 response carries the Type-2 blob","Treat an absent header as a negative result rather than an error"],"exampleFix":"// before\nconst info = http.DecodeNTLM(resp.GetHeader('WWW-Authenticate')); // -> ntlm: empty blob\n\n// after: check the challenge exists, then decode\nconst challenge = resp.GetHeader('WWW-Authenticate');\nif (challenge && challenge.trim()) {\n  const info = http.DecodeNTLM(challenge);\n  log(info.DNSComputerName);\n}","handlingStrategy":"validation","validationCode":"const challenge = resp.GetHeader('WWW-Authenticate');\nif (challenge && challenge.trim()) {\n  const info = http.DecodeNTLM(challenge);\n  log(info.DNSComputerName);\n} else {\n  // no NTLM challenge offered: negative result, skip decoding\n}","typeGuard":"const hasNtlmChallenge = (h) => /NTLM|Negotiate/i.test(String(h || '').trim());","tryCatchPattern":"try { const info = http.DecodeNTLM(header); }\ncatch (e) { if (/ntlm: empty blob/.test(e.message || '')) { /* target offered no NTLM: treat as negative, not an error */ } }","preventionTips":["Always check the WWW-Authenticate header exists before decoding","Send an NTLM negotiate first (SetHeader with NegotiateNTLM()) to force the 401 challenge","Treat missing NTLM support as a scan outcome, not an exception"],"tags":["ntlm","http","javascript","validation"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}