hunt-ntlm-info
Hunt NTLM/Negotiate information disclosure on internet-reachable IIS/SharePoint/Exchange. Anonymous NTLM Type-2 challenge capture leaks NetBIOS domain, internal DNS forest, computer name, AD timestamp via AV_PAIRS structure. Default Windows-installer hostnames (WIN-XXXXXXXXXXX pattern) signal lazy provisioning. Use when target advertises `WWW-Authenticate: NTLM` or `Negotiate` headers anonymously.
适合你,如果需要在互联网可达的 Windows 服务上收集 NTLM 认证信息。
用别的 agent?下载 .zip 解压,把文件夹放进它的技能目录
~/.claude/skills/(项目级 .claude/skills/)~/.codex/skills/npx oh-my-skill add elementalsouls/claude-bughunter/hunt-ntlm-infocurl -fsSL https://oh-my-skill.com/install.sh | bash -s -- elementalsouls/claude-bughunter/hunt-ntlm-infonpx oh-my-skill verify elementalsouls/claude-bughunter/hunt-ntlm-info怎么用
商店整理自技能原文 · 版本 05098fc · 表述以原文为准装上后,Claude 会主动探测目标服务器是否泄露内部AD域名、计算机名、时间戳等敏感信息,并解析NTLM Type-2挑战中的AV_PAIRS结构。
当目标服务器在HTTP响应头中匿名返回`WWW-Authenticate: NTLM`或`Negotiate`时触发。
技能原文 SKILL.md
Crown Jewel Targets
NTLM info disclosure is a Medium-severity finding when chained to context — the leak itself is intentional protocol behavior (RFC-compliant NTLMSSP challenge), but on internet-exposed enterprise infrastructure it provides exact reconnaissance for the next stage of an attack. Highest-value targets:
- Internet-reachable IIS / SharePoint / Exchange / OWA with dual-auth (Forms + NTLM, or NTLM + Kerberos)
- Citrix NetScaler / VMware Horizon View internet-facing gateways with NTLM-backed AD auth
- Lync / Skype for Business / Teams On-Prem edge servers
- WSUS / Windows Update Services with NTLM-protected admin paths
- CIFS-style fileshare proxies (HCL Sametime, IBM Notes Domino) that proxy NTLM
- Legacy SharePoint farms that left NTLM enabled on the public-zone IIS binding
What makes this pay:
- Internal AD domain disclosure (parent-forest mapping, e.g.
customer.parent-corp.example→ tenant inside corporate-AD tree) - Default-Windows-hostname disclosure (
WIN-XXXXXXXXXXXpattern signals rushed provisioning → likely default service-account passwords) - Timestamp leak (used in NTLMv2 hash cracking acceleration)
- Direct attack-map enrichment for credential spraying combined with
hunt-auth-bypassLegacy-Protocol Matrix
Attack Surface Signals
Response headers signaling NTLM availability:
WWW-Authenticate: NTLM WWW-Authenticate: Negotiate WWW-Authenticate: NTLM, Negotiate WWW-Authenticate: Negotiate, NTLM
URL patterns where NTLM is commonly exposed:
/_api/web/CurrentUser (SharePoint REST) /_vti_bin/*.asmx (SharePoint legacy SOAP) /EWS/Exchange.asmx (Exchange Web Services) /Autodiscover/Autodiscover.xml (Exchange autodiscover) /owa/ (Outlook Web App) /Microsoft-Server-ActiveSync (ActiveSync) /PowerShell (Exchange Mgmt Shell over HTTPS) /api/v3/ (TeamCity, Atlassian) /wsus/ (Windows Server Update Services) /manager/html (some Tomcat behind IIS) /iisstart.htm (default IIS, sometimes reveals NTLM upstream)
Tech-stack signals:
- IIS on the public internet (almost always NTLM-capable, even if Forms is the front)
- SharePoint Web Front End (almost always dual-auth Forms + NTLM)
- Exchange edge transport
- Server header
Microsoft-HTTPAPI/2.0,Microsoft-IIS/*,IIS/*
Step-by-Step Hunting Methodology
- Probe every anonymous endpoint for
WWW-Authenticate: NTLM. Send a vanilla GET and inspect response headers. If NTLM is offered, proceed.
- Send a valid NTLMSSP Type-1 message anonymously. The Type-1 base64 below requests NetBIOS-domain and Workstation info from the server: ``` Authorization: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw== ``` This is the standard test Type-1 with negotiate flags
NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_TARGET_INFO. TheOS Versionfield (06 01 B1 1D 00 00 00 0F) is Windows 7 build 7601 — accepted by virtually every NTLM responder.
- Use a keep-alive raw socket, not Python requests / curl one-shot. Most HTTP libraries close the connection between the Type-1 send and Type-2 reception. Use one of:
- Burp Repeater with
Connection: keep-aliveset explicitly - Burp
mcp__burp__send_http1_request(handles keep-alive natively) - Python raw
socket+ssl.wrap_socket(see Payload section)
- Parse the Type-2 challenge from the
WWW-Authenticate: NTLM <base64>response header. Base64-decode the value. The structure is NTLMSSP per MS-NLMP: - Bytes 0-7: literal
NTLMSSP\0 - Bytes 8-11: MessageType =
\x02\x00\x00\x00 - Bytes 12-19: TargetName SecurityBuffer (len, alloc, offset)
- Bytes 20-23: NegotiateFlags
- Bytes 24-31: Server Challenge (8 bytes — useful for offline cracking)
- Bytes 40-47: TargetInfo SecurityBuffer (len, alloc, offset)
- TargetInfo body:
AV_PAIRSarray of (AvId u16, AvLen u16, Value)
- Decode the AV_PAIRS. The AvIds you care about:
1= NetBIOS Computer Name2= NetBIOS Domain Name3= DNS Computer Name (FQDN of the responding server)4= DNS Domain Name (the AD domain)5= DNS Tree Name (the AD forest root)7= Timestamp (FILETIME, useful for NTLMv2 hash relay / cracking)9= Target Name (in newer NTLMSSP)
- Map findings to severity tier:
- Internet-exposed + default
WIN-XXXXXXXXXXXhostname + corporate-AD-tree disclosure → Medium - Internet-exposed + named-server hostname (
SPWEB01.corp.example) + corporate-AD-tree → Low-Medium - Intranet-only + any disclosure → Informational
- Combine with
hunt-auth-bypassLegacy-Protocol Matrix findings on the same host → upgrade the auth-bypass finding's severity since the attacker has UPN/SAM format ready
- Check the timestamp. If
AV[7]returns a current FILETIME within ~5s ofDate:header, the system clock is synced — useful intel for Kerberos golden-ticket forging (out of bug-bounty scope but red-team relevant).
- Cross-reference with subdomain enum. The DNS Tree name often reveals the parent forest — e.g.
customer.parent-corp.examplereveals the customer is a sub-domain INSIDE corporate-parent AD, not a separate tenant. This is a privacy / topology-disclosure escalation that programs sometimes accept as Medium.
Payload & Detection Patterns
Generic NTLM Type-1 anonymous probe (curl + raw socket fallback):
# Most one-shot curl runs DON'T return Type-2 because the connection closes. # Use this as a quick probe to confirm NTLM is offered: curl -sk -I -H "Authorization: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==" \ "https://target.example/_api/web/CurrentUser" 2>&1 | grep -i "WWW-Authenticate"
Burp send_http1_request (recommended for full Type-2 capture):
GET /_api/web/CurrentUser HTTP/1.1 Host: target.example Authorization: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw== Connection: keep-alive User-Agent: Mozilla/5.0
Python raw socket + AV_PAIR decoder:
import socket, ssl, base64, struct, re
from datetime import datetime, timezone
HOST = "target.example"
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(socket.create_connection((HOST, 443)), server_hostname=HOST)
s.sendall(
f"GET /_api/web/CurrentUser HTTP/1.1\r\n"
f"Host: {HOST}\r\n"
"Authorization: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==\r\n"
"User-Agent: Mozilla/5.0\r\nConnection: keep-alive\r\n\r\n".encode()
)
data = b""
while True:
chunk = s.recv(8192)
if not chunk: break
data += chunk
if b"\r\n\r\n" in data: break
m = re.search(rb"WWW-Authenticate:\s*NTLM\s+([A-Za-z0-9+/=]{20,})", data, re.I)
if m:
b = base64.b64decode(m.group(1).decode("ascii"))
assert b[:8] == b"NTLMSSP\x00"
tn_len, _, tn_off = struct.unpack_from('<HHI', b, 12)
ti_len, _, ti_off = struct.unpack_from('<HHI', b, 40)
print(f"TargetName: {b[tn_off:tn_off+tn_len].decode('utf-16-le', errors='ignore')!r}")
av_types = {1:'NetBIOS Computer Name', 2:'NetBIOS Domain Name',
3:'DNS Computer Name', 4:'DNS Domain Name',
5:'DNS Tree Name', 7:'Timestamp', 9:'Target Name'}
i = 0
ti = b[ti_off:ti_off+ti_len]
while i < len(ti):
av_id, av_len = struct.unpack_from('<HH', ti, i)
if av_id == 0: break
val = ti[i+4:i+4+av_len]
if av_id == 7:
ts = struct.unpack('<Q', val[:8])[0]
secs = (ts - 116444736000000000) / 10000000
vs = datetime.fromtimestamp(secs, tz=timezone.utc).isoformat()
else:
vs = val.decode('utf-16-le', errors='ignore')
print(f" AV[{av_id}] {av_types.get(av_id, '?'):28s}: {vs!r}")
i += 4 + av_len
Burp Collaborator NOT needed for this finding class — the data leak is in the synchronous response, not via OOB.
Common Root Causes
- Dual-auth IIS bindings on the public zone. Administrators leave NTLM enabled on the public-facing IIS site even when Forms auth is the intended entry point. Internal users get SSO; external attackers get the AD topology leak.
- Default IIS Application Pool identity left as
ApplicationPoolIdentity. Combined with default hostname, signals provisioning never went past first-boot.
- Server never renamed from Windows-installer-generated hostname. Microsoft's default
WIN-XXXXXXXXXXX11-character pattern is the immediate tell. Sometimes alsoWORKGROUP\WIN-...in older boxes.
- Sub-domain joined to corporate forest without zone-isolation. European-integrator case: a a European importer's SharePoint test environment is a child domain inside a corporate global AD, disclosed via NTLM DNS Tree Name. The customer probably intends
customer.parent-corp.exampleto be operationally separate but the NTLM Type-2 reveals the forest membership to anyone who probes.
- IIS Extended Protection NOT enabled. When
<system.webServer><security><authentication><windowsAuthentication extendedProtection>isNone(the default), the NTLM challenge is sent to any anonymous client. When set toRequired, NTLM is restricted to authenticated callers — and the AV-pair leak is mitigated.
- No
WindowsAuthenticationremoved fromapplicationHost.configfor internet-exposed sites. SharePoint Central Admin sometimes leaves this enabled even when SP zone configuration only enables Forms.
Bypass Techniques
This skill describes a disclosure leak, not an authentication bypass. The "bypass" question is: how do defenders block this AV-pair leak while still allowing legitimate NTLM auth?
| Defense | Effectiveness | |---|---| | Disable NTLM on the public IIS binding entirely (Forms-only) | Best — eliminates the surface | | IIS Extended Protection = Required | Restricts NTLM challenge to authenticated callers; AV-pair leak mitigated | | Reverse-proxy strip WWW-Authenticate from anonymous responses | Sometimes works but breaks legitimate clients | | Rate-limit the Type-1 → Type-2 endpoint | Doesn't prevent disclosure, only slows enumeration | | Rename the Windows host from WIN-XXXXXXXXXXX | Removes the "lazy provisioning" tell; doesn't stop the leak | | Move the SP/Exchange farm to a child AD with no cross-trust to corporate | Mitigates the forest disclosure; doesn't stop the leak |
For the attacker: there's no "bypass" needed — the leak is the finding.
Gate 0 Validation
Before writing the report, confirm:
- What can the attacker do RIGHT NOW with this disclosure?
- Internet-exposed + default hostname + corporate forest disclosed → Medium: attacker has UPN format for
hunt-auth-bypassmatrix probes, plus knows server has likely-default service accounts. - Intranet-only or only NetBIOS name → Informational.
- Does the program accept information-disclosure findings without a chained impact?
- Many programs (Microsoft, large enterprise VDPs) DO accept this when the leaked info includes internal AD topology.
- Many programs (Shopify, GitHub) reject info disclosure without a chained impact.
- Read the program scope before submitting; if borderline, chain with a Tier-A finding from
hunt-auth-bypass.
- Can you reproduce in <5 minutes from a fresh shell?
- The Python snippet above is the canonical reproduction. Include it verbatim in the report.
Real Impact Examples
Scenario A — Enterprise SharePoint inside parent corporate AD
Target: https://target-portal.example/ — a enterprise dealer portal (test mirror) operated by a system integrator.
Sending the anonymous Type-1 message to /_api/web/CurrentUser returned a Type-2 challenge whose AV_PAIRS decoded to:
NetBIOS Domain Name: <CustomerName> NetBIOS Computer Name: WIN-XXXXXXXXXXX DNS Domain Name: customer.parent-corp.example DNS Computer Name: WIN-XXXXXXXXXXX.customer.parent-corp.example DNS Tree Name: customer.parent-corp.example Timestamp: 2026-05-13T15:55:37.922Z
Three escalation paths:
- Default Windows-installer hostname (
WIN-XXXXXXXXXXX) — server was never renamed after OS install; strong signal of lazy provisioning. Likely default service-account passwords on the SQL backend, default WSUS config, etc. - Sub-domain inside corporate-parent AD (
customer.parent-corp.example) — the customer is a child domain inside <ParentCorp>'s global Active Directory. A compromise of this test farm has potential cross-trust to corporate-parent. - UPN format known — combined with
hunt-auth-bypass's discovery of an anonymous brute-force endpoint on/_vti_bin/Authentication.asmx, the attacker has both the credential format (firstname.lastname@customer.parent-corp.exampleor<CustomerName>\firstname.lastname) and the unlimited submission endpoint.
Reported severity: Medium, with a note that the chain with the Authentication.asmx anonymous brute-force makes the combined attack Critical.
Scenario B — Exchange edge with NTLM-protected EWS
Target: https://mail.example.com/EWS/Exchange.asmx. Type-1 probe returns Type-2 with DNS Tree Name corp.example.com and DNS Computer Name MAIL01.corp.example.com. Confirms the Exchange edge is domain-joined to corporate AD (rather than running in a DMZ-isolated AD). For an attacker with the matching hunt-mfa-bypass / hunt-auth-bypass chain, the leaked UPN format and server-name format accelerate credential spraying by removing the recon step. Reported severity: Low-Medium depending on program.
Scenario C — Intranet-only intentional leak (not a finding)
Target: https://intranet.corp.example (clearly internal, behind VPN). Type-1 returns full AV-pair set. Not reportable — this is intended NTLM behavior on intranet, and the disclosure is to authenticated VPN users who already see the same data via nltest /dsgetdc:corp.example.com. Recognize and drop.
Related Skills & Chains
hunt-sharepoint— SharePoint farms emit anonymous Type-2 challenges on/_vti_bin/by default; this is one of the most reliable ways to get internal AD topology. Chain primitive: SharePoint discovered → NTLM Type-2 capture on/_vti_bin/Lists.asmx→hunt-ntlm-infoAV_PAIR decode → internal forest name →m365-entra-attackROPC spray on Entra tenant tied to that forest.m365-entra-attack— Leaked NetBIOS domain + UPN suffix is the missing piece for a credible password spray. Chain primitive: NTLM Type-2 yieldscorp.example.comDNS tree → cross-reference Entra tenant viahttps://login.microsoftonline.com/corp.example.com/.well-known/openid-configuration→m365-entra-attackAADSTS error-differential username enumeration on resolved tenant.hunt-aspnet— IIS sites running ASP.NET frequently expose NTLM on management paths. Chain primitive: NTLM Type-2 on/owa/,/ecp/,/rpc/,/aspnet_client/→ confirm IIS + ASP.NET version →hunt-aspnetViewState /.axdenumeration on same host.offensive-osint— The hostname patternWIN-XXXXXXXXXXXsignals lazy provisioning and predicts other weak hygiene. Chain primitive: NTLM Type-2 returns default-installer hostname → flag as low-maturity environment →offensive-osintdeep recon (cert transparency, GitHub leakage, breach corpus correlation) is high-yield on this org.triage-validation— Most NTLM info-disclosure findings die at the 7-Question Gate on "is this exploitable" — pure topology disclosure is Low/Informational. Chain primitive: pull every NTLM-info finding throughtriage-validationBEFORE writing it up; only report if (a) leaks UPN format that accelerates spray, or (b) leaks production hostname mapping (redteam-report-templatefor the chain-narrative).