关于 detecting-ntlm-relay-with-event-correlation
这项安全技能通过事件与网络关联,帮助检测 Active Directory 中的 NTLM 中继活动,适用于防御人员和获授权的安全测试人员。内容涵盖认证信息不匹配、投毒迹象、签名配置及 NTLM 降级检测。
主要功能
- 01关联 Windows 认证事件
- 02检测 IP 与主机名不匹配
- 03识别 LLMNR 和 NBT-NS 投毒迹象
- 04审计 SMB 与 LDAP 签名
- 05检测 NTLMv1 降级活动
适用场景
- 01狩猎 Active Directory 中的 NTLM 中继
- 02调查来自意外 IP 的认证
- 03在获授权演练中验证中继检测
- 04评估 SMB 与 LDAP 签名执行情况
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill detecting-ntlm-relay-with-event-correlation安装后在 Claude Code 中输入/detecting-ntlm-relay-with-event-correlation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.pyaudit_smb_signing.ps1detect_ntlm_relay.py详细介绍
通过事件关联检测 NTLM 中继
授权测试免责声明:本技能中描述的攻防技术和攻击模拟仅用于经系统所有者明确书面许可的授权渗透测试、红队演练、紫队演习和安全研究。未经授权对您不拥有或未获许可测试的系统使用这些技术是非法的且不道德的。请始终在您的交战范围内操作,并遵守适用的法律法规。
概述
NTLM 中继攻击拦截 NTLM 身份验证消息并将其转发到目标服务以获取未授权访问。攻击者使用 Responder 等工具进行 LLMNR/NBT-NS/mDNS 投毒,使用 ntlmrelayx(Fox-IT/Impacket)进行多协议中继,并使用 PetitPotam(MS-EFSRPC)和 DFSCoerce 等强制认证技术从域控制器等高价值目标强制发起身份验证。本技能提供了一个全面的事件关联框架,利用 Windows 安全事件 4624 LogonType 3 分析、IP 与主机名不匹配检测、Responder 流量识别、SMB/LDAP 签名审计以及 NTLM 降级检测,来识别 Active Directory 环境中的中继攻击。
何时使用
- 在仍在使用 NTLM 身份验证的 Active Directory 环境中搜寻凭据中继活动
- 调查源 IP 与预期工作站不匹配的身份验证异常告警
- 审计 SMB 签名和 LDAP 签名强制实施情况,以评估对中继攻击的暴露程度
- 检测 NTLM 降级攻击,其中 NTLMv2 被强制降级为 NTLMv1,以便更轻松地进行离线破解或中继
- 为 MITRE ATT&CK T1557.001(LLMNR/NBT-NS 投毒和 SMB 中继)构建 SIEM 关联规则
- 响应可能先于中继攻击的 PetitPotam、DFSCoerce 或 PrinterBug 强制认证告警
- 在紫队演习期间验证 NTLM 中继检测和 SMB 签名强制实施
请勿使用于没有集中式 Windows 安全事件日志收集的场景,作为强制实施 SMB 签名和身份验证扩展保护(EPA)的替代方案(这些措施在协议层面阻止中继攻击),或没有用于关联的 IP 与主机名清单的情况。
前提条件
- 配置 Windows 高级审核策略以捕获事件 ID 4624、4625、4648、4776 和 8004
- 通过 Windows 事件转发(WEF)或基于 Agent 的传输将日志集中收集到 SIEM
- 具备关联和告警能力的 SIEM 平台(Splunk、Elastic、Microsoft Sentinel)
- IP 地址到主机名的映射清单(DHCP 日志、DNS 记录或 CMDB)
- 对 LLMNR(UDP 5355)、NBT-NS(UDP 137)和 mDNS(UDP 5353)流量进行网络监控
- 了解 MITRE ATT&CK T1557.001 和 T1187(强制认证)
工作流
步骤 1:理解 NTLM 中继攻击流程
NTLM 中继攻击遵循三阶段模式:强制认证/投毒、拦截和中继。
阶段 1 -- 强制认证或投毒:攻击者强制或诱骗受害者发起 NTLM 身份验证。方法包括 LLMNR/NBT-NS 投毒(Responder)、PetitPotam(MS-EFSRPC 滥用)、PrinterBug(SpoolService)和 DFSCoerce。
阶段 2 -- 拦截:攻击者从受害者处捕获 NTLM Type 1(Negotiate)和 Type 3(Authenticate)消息。
阶段 3 -- 中继:攻击者将捕获的 NTLM 消息转发到目标服务(SMB、LDAP、HTTP、MSSQL),以受害者的身份进行身份验证。仅当未强制实施消息签名时,此操作才会成功。
Victim ──NTLM Negotiate──> Attacker ──NTLM Negotiate──> Target
Victim <──NTLM Challenge── Attacker <──NTLM Challenge── Target
Victim ──NTLM Authenticate──> Attacker ──NTLM Authenticate──> Target
↓
Attacker authenticated
as Victim on Target
关键检测洞察:在中继攻击中,目标上的事件 4624 将显示受害者的用户名,但显示攻击者的 IP 地址。WorkstationName 字段可能仍反映受害者的机器。这种 IP 与主机名的不匹配是主要的检测信号。
步骤 2:用于中继检测的事件 4624 LogonType 3 分析
# Splunk: Detect IP-to-Hostname Mismatches in Network Logons
# Core NTLM relay detection -- correlates WorkstationName with IpAddress
index=wineventlog EventCode=4624 LogonType=3
AuthenticationPackageName="NTLM" LmPackageName="NTLM V2"
| where TargetUserName != "ANONYMOUS LOGON"
AND TargetUserName != "-"
AND NOT match(TargetUserName, ".*\\$$")
| eval workstation_lower=lower(WorkstationName)
| lookup dns_inventory.csv hostname AS workstation_lower OUTPUT expected_ip
| where isnotnull(expected_ip) AND IpAddress != expected_ip
| table _time ComputerName TargetUserName WorkstationName IpAddress expected_ip
LogonProcessName AuthenticationPackageName
| sort -_time
| rename ComputerName as TargetHost, IpAddress as ActualSourceIP,
expected_ip as ExpectedSourceIP
# Splunk: Detect Rapid Multi-Host Authentication (Relay Spraying)
# Attackers relay captured credentials to multiple targets quickly
index=wineventlog EventCode=4624 LogonType=3
AuthenticationPackageName="NTLM"
| where TargetUserName != "ANONYMOUS LOGON"
AND NOT match(TargetUserName, ".*\\$$")
| bin _time span=2m
| stats dc(ComputerName) as target_count values(ComputerName) as targets
values(IpAddress) as source_ips by _time TargetUserName
| where target_count > 3
| table _time TargetUserName source_ips target_count targets
| sort -target_count
# Splunk: Detect NTLM Authentication from Non-Workstation IPs
# Relay tools often run from Linux attack boxes not in DNS/DHCP inventory
index=wineventlog EventCode=4624 LogonType=3
AuthenticationPackageName="NTLM"
| where TargetUserName != "ANONYMOUS LOGON"
AND NOT match(TargetUserName, ".*\\$$")
| lookup dhcp_leases.csv ip AS IpAddress OUTPUT mac_address hostname
| where isnull(hostname)
| stats count dc(ComputerName) as targets_hit values(ComputerName) as target_hosts
by IpAddress TargetUserName WorkstationName
| where count > 1
| table IpAddress TargetUserName WorkstationName targets_hit target_hosts count
| sort -targets_hit
-- Microsoft Sentinel KQL: NTLM Relay Detection via IP-Hostname Mismatch
let known_hosts = datatable(WorkstationName:string, ExpectedIP:string)
[
// Populate from CMDB or use DeviceNetworkInfo table
];
SecurityEvent
| where EventID == 4624 and LogonType == 3
| where AuthenticationPackageName == "NTLM"
| where TargetUserName !endswith "$"
| where TargetUserName != "ANONYMOUS LOGON"
| where IpAddress != "-" and IpAddress != "::1" and IpAddress != "127.0.0.1"
| extend WorkstationClean = toupper(trim_end(@"\s+", WorkstationName))
| join kind=inner (known_hosts) on WorkstationName
| where IpAddress != ExpectedIP
| project TimeGenerated, Computer, TargetUserName, WorkstationName,
IpAddress, ExpectedIP, LogonProcessName, AuthenticationPackageName,
LmPackageName
| sort by TimeGenerated desc
-- Microsoft Sentinel KQL: Rapid NTLM Authentication to Multiple Targets
SecurityEvent
| where EventID == 4624 and LogonType == 3
| where AuthenticationPackageName == "NTLM"
| where TargetUserName !endswith "$"
| where TargetUserName != "ANONYMOUS LOGON"
| summarize TargetCount=dcount(Computer),
Targets=make_set(Computer),
SourceIPs=make_set(IpAddress),
AuthCount=count()
by TargetUserName, bin(TimeGenerated, 2m)
| where TargetCount > 3
| project TimeGenerated, TargetUserName, SourceIPs, TargetCount, Targets, AuthCount
| sort by TargetCount desc
步骤 3:通过网络和事件分析进行 Responder 检测
# Splunk: Detect Responder LLMNR/NBT-NS Poisoning via Network Logs
# Responder answers LLMNR (UDP 5355) and NBT-NS (UDP 137) queries
index=network sourcetype=zeek_dns
| where query_type IN ("LLMNR", "NBNS")
OR id.resp_p IN (5355, 137)
| stats dc(id.orig_h) as victims count by id.resp_h answers
| where count > 10
| rename id.resp_h as responder_ip
| table responder_ip victims answers count
| sort -count
# Splunk: Detect LLMNR/NBT-NS Response from Non-DNS Servers
# Legitimate DNS servers respond to these; Responder impersonates them
index=network sourcetype="bro:dns:json" OR sourcetype="zeek:conn:json"
| where id_resp_p=5355 OR id_resp_p=137
| where NOT cidrmatch("10.10.0.0/24", id_resp_h)
| stats count dc(id_orig_h) as unique_victims by id_resp_h
| where unique_victims > 3
| table id_resp_h unique_victims count
| rename id_resp_h as suspicious_responder
# PowerShell: Detect LLMNR and NBT-NS activity on local network
# Run on a monitoring host to identify Responder-like behavior
# Check if LLMNR is disabled (should be disabled to prevent poisoning)
$llmnr = Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" `
-Name "EnableMulticast" -ErrorAction SilentlyContinue
Write-Host "[*] LLMNR Status: $(if ($llmnr.EnableMulticast -eq 0) { 'DISABLED (Good)' } else { 'ENABLED (Vulnerable to Responder)' })"
# Check if NBT-NS is disabled
$adapters = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True"
foreach ($adapter in $adapters) {
$nbtns = $adapter.TcpipNetbios
$status = switch ($nbtns) {
0 { "Default (Enabled)" }
1 { "Enabled" }
2 { "Disabled (Good)" }
}
Write-Host "[*] Adapter '$($adapter.Description)' NBT-NS: $status"
}
# Query Windows Firewall logs for LLMNR/NBT-NS traffic
Get-WinEvent -LogName "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall" `
-MaxEvents 1000 -ErrorAction SilentlyContinue |
Where-Object {
$_.Message -match "5355|137" -and $_.Message -match "UDP"
} |
Select-Object TimeCreated, @{N='Detail';E={$_.Message.Substring(0,200)}} |
Format-Table -AutoSize
# Sigma Rule: Responder LLMNR/NBT-NS Poisoning Detection
title: Potential Responder LLMNR/NBT-NS Poisoning Activity
id: 7a8b9c0d-e1f2-3a4b-5c6d-7e8f9a0b1c2d
status: stable
description: >
Detects a single host responding to LLMNR (UDP 5355) or NBT-NS (UDP 137)
queries from multiple unique sources, indicating possible Responder poisoning.
references:
- https://www.hackthebox.com/blog/ntlm-relay-attack-detection
- https://blog.fox-it.com/2017/05/09/relaying-credentials-everywhere-with-ntlmrelayx/
logsource:
category: firewall
detection:
selection:
dst_port:
- 5355
- 137
action: allow
condition: selection | count(src_ip) by dst_ip > 5
timeframe: 5m
level: high
tags:
- attack.credential_access
- attack.t1557.001
falsepositives:
- Legitimate WINS servers or DNS servers responding to broadcast queries
- Network discovery tools performing name resolution
步骤 4:SMB 签名强制审计
# PowerShell: Audit SMB Signing Status Across Domain
# SMB signing prevents NTLM relay to SMB services
# Check local SMB signing configuration
Write-Host "=== LOCAL SMB SIGNING STATUS ==="
$smbServer = Get-SmbServerConfiguration
Write-Host "[*] SMB Server RequireSecuritySignature: $($smbServer.RequireSecuritySignature)"
Write-Host "[*] SMB Server EnableSecuritySignature: $($smbServer.EnableSecuritySignature)"
$smbClient = Get-SmbClientConfiguration
Write-Host "[*] SMB Client RequireSecuritySignature: $($smbClient.RequireSecuritySignature)"
Write-Host "[*] SMB Client EnableSecuritySignature: $($smbClient.EnableSecuritySignature)"
# Check via registry (works on older systems)
$serverSigning = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanManServer\Parameters" `
-Name "RequireSecuritySignature" -ErrorAction SilentlyContinue
$clientSigning = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanManWorkstation\Parameters" `
-Name "RequireSecuritySignature" -ErrorAction SilentlyContinue
Write-Host "`n=== REGISTRY VALUES ==="
Write-Host "[*] Server RequireSecuritySignature: $($serverSigning.RequireSecuritySignature) (1=Required, 0=Not Required)"
Write-Host "[*] Client RequireSecuritySignature: $($clientSigning.RequireSecuritySignature) (1=Required, 0=Not Required)"
# PowerShell: Domain-Wide SMB Signing Audit
# Scan all domain computers for SMB signing enforcement
$domainComputers = Get-ADComputer -Filter * -Properties OperatingSystem |
Where-Object { $_.OperatingSystem -like "*Windows*" -and $_.Enabled -eq $true } |
Select-Object -ExpandProperty DNSHostName
$results = @()
foreach ($computer in $domainComputers) {
try {
$session = New-CimSession -ComputerName $computer -ErrorAction Stop
$smbConfig = Get-SmbServerConfiguration -CimSession $session -ErrorAction Stop
$results += [PSCustomObject]@{
Computer = $computer
RequireSigning = $smbConfig.RequireSecuritySignature
EnableSigning = $smbConfig.EnableSecuritySignature
Status = if ($smbConfig.RequireSecuritySignature) { "ENFORCED" } else { "VULNERABLE" }
}
Remove-CimSession $session
} catch {
$results += [PSCustomObject]@{
Computer = $computer
RequireSigning = "ERROR"
EnableSigning = "ERROR"
Status = "UNREACHABLE"
}
}
}
# Display results sorted by vulnerability
$results | Sort-Object Status | Format-Table -AutoSize
# Export vulnerable hosts
$vulnerable = $results | Where-Object { $_.Status -eq "VULNERABLE" }
Write-Host "`n[!] VULNERABLE HOSTS (SMB Signing Not Required): $($vulnerable.Count)"
$vulnerable | Export-Csv -Path "smb_signing_audit.csv" -NoTypeInformation
# PowerShell: Audit LDAP Signing Status on Domain Controllers
# LDAP signing prevents NTLM relay to LDAP/LDAPS services
# Check LDAP signing requirement on domain controllers
$dcs = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName
foreach ($dc in $dcs) {
# Check LDAP server signing requirement
$ldapSigning = Invoke-Command -ComputerName $dc -ScriptBlock {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters"
$value = Get-ItemProperty -Path $regPath -Name "LDAPServerIntegrity" -ErrorAction SilentlyContinue
return $value.LDAPServerIntegrity
} -ErrorAction SilentlyContinue
$status = switch ($ldapSigning) {
0 { "NONE (Vulnerable)" }
1 { "Negotiate Signing (Default - Vulnerable to relay)" }
2 { "Require Signing (Secure)" }
default { "Unknown/Error" }
}
Write-Host "[*] $dc LDAP Signing: $status"
# Check LDAP channel binding
$channelBinding = Invoke-Command -ComputerName $dc -ScriptBlock {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters"
$value = Get-ItemProperty -Path $regPath -Name "LdapEnforceChannelBinding" -ErrorAction SilentlyContinue
return $value.LdapEnforceChannelBinding
} -ErrorAction SilentlyContinue
$cbStatus = switch ($channelBinding) {
0 { "Disabled (Vulnerable)" }
1 { "When Supported" }
2 { "Always Required (Secure)" }
default { "Not Configured (Vulnerable)" }
}
Write-Host "[*] $dc LDAP Channel Binding: $cbStatus"
}
# Splunk: Monitor for SMB sessions without signing
# Requires Zeek SMB logging or packet capture analysis
index=network sourcetype="zeek:smb_mapping:json" OR sourcetype="bro:smb_mapping:json"
| where NOT security_mode="signing_required"
| stats count dc(id_orig_h) as unique_clients by id_resp_h security_mode
| sort -unique_clients
| rename id_resp_h as smb_server
| table smb_server security_mode unique_clients count
步骤 5:NTLM 降级检测
# Splunk: Detect NTLMv1 Authentication (Downgrade from NTLMv2)
# NTLMv1 is weaker and easier to relay/crack -- should not be in use
index=wineventlog EventCode=4624 LogonType=3
LmPackageName="NTLM V1"
| where TargetUserName != "ANONYMOUS LOGON"
AND NOT match(TargetUserName, ".*\\$$")
| stats count values(ComputerName) as targets
values(IpAddress) as source_ips
by TargetUserName LmPackageName
| table TargetUserName LmPackageName source_ips targets count
| sort -count
# Splunk: Detect NTLM Downgrade Attack Pattern
# NTLMv1 appearing after a period of only NTLMv2 suggests active downgrade
index=wineventlog EventCode=4624 LogonType=3
AuthenticationPackageName="NTLM"
| where TargetUserName != "ANONYMOUS LOGON"
| bin _time span=1h
| stats count(eval(LmPackageName="NTLM V1")) as ntlmv1_count
count(eval(LmPackageName="NTLM V2")) as ntlmv2_count
by _time
| where ntlmv1_count > 0
| eval ntlmv1_ratio = round(ntlmv1_count / (ntlmv1_count + ntlmv2_count) * 100, 2)
| table _time ntlmv1_count ntlmv2_count ntlmv1_ratio
| sort -_time
-- Microsoft Sentinel KQL: NTLMv1 Downgrade Detection
SecurityEvent
| where EventID == 4624 and LogonType == 3
| where AuthenticationPackageName == "NTLM"
| where LmPackageName == "NTLM V1"
| where TargetUserName !endswith "$"
| where TargetUserName != "ANONYMOUS LOGON"
| project TimeGenerated, Computer, TargetUserName, WorkstationName,
IpAddress, LmPackageName, LogonProcessName
| sort by TimeGenerated desc
# PowerShell: Detect NTLMv1 Authentication Events on Local System
$ntlmv1Events = Get-WinEvent -LogName Security -FilterXPath @"
*[System[(EventID=4624)]]
and
*[EventData[Data[@Name='LmPackageName']='NTLM V1']]
"@ -MaxEvents 500 -ErrorAction SilentlyContinue
if ($ntlmv1Events.Count -gt 0) {
Write-Host "[!] WARNING: $($ntlmv1Events.Count) NTLMv1 authentication events detected!" -ForegroundColor Red
$ntlmv1Events | ForEach-Object {
$xml = [xml]$_.ToXml()
$eventData = $xml.Event.EventData.Data
[PSCustomObject]@{
Time = $_.TimeCreated
TargetUser = ($eventData | Where-Object { $_.Name -eq "TargetUserName" }).'#text'
Workstation = ($eventData | Where-Object { $_.Name -eq "WorkstationName" }).'#text'
SourceIP = ($eventData | Where-Object { $_.Name -eq "IpAddress" }).'#text'
LmPackage = ($eventData | Where-Object { $_.Name -eq "LmPackageName" }).'#text'
}
} | Format-Table -AutoSize
} else {
Write-Host "[+] No NTLMv1 authentication events found (Good)" -ForegroundColor Green
}
# Audit GPO settings for NTLM restriction
Write-Host "`n=== NTLM RESTRICTION POLICY ==="
$ntlmPolicy = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
-Name "LmCompatibilityLevel" -ErrorAction SilentlyContinue
$level = switch ($ntlmPolicy.LmCompatibilityLevel) {
0 { "Send LM & NTLM responses (Most Vulnerable)" }
1 { "Send LM & NTLM - use NTLMv2 session security if negotiated" }
2 { "Send NTLM response only" }
3 { "Send NTLMv2 response only (Recommended minimum)" }
4 { "Send NTLMv2 response only, refuse LM" }
5 { "Send NTLMv2 response only, refuse LM & NTLM (Most Secure)" }
default { "Not configured (defaults to 3 on modern Windows)" }
}
Write-Host "[*] LmCompatibilityLevel: $($ntlmPolicy.LmCompatibilityLevel) - $level"
步骤 6:NTLM 审计和限制策略配置
# PowerShell: Enable NTLM Auditing via Group Policy Registry Settings
# Must be applied via GPO for domain-wide coverage
# Audit all NTLM authentication in this domain
# GPO: Computer Configuration > Policies > Windows Settings > Security Settings >
# Local Policies > Security Options >
# Network Security: Restrict NTLM: Audit NTLM authentication in this domain = Enable all
# Registry equivalent (apply via GPO preferences or startup script)
# Domain Controller setting:
# Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" `
# -Name "AuditNTLMInDomain" -Value 7 -Type DWord
# Audit incoming NTLM traffic on all servers:
# Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0" `
# -Name "AuditReceivingNTLMTraffic" -Value 2 -Type DWord
# After enabling auditing, NTLM events appear in:
# Applications and Services Logs > Microsoft > Windows > NTLM > Operational
# Query NTLM operational log for audit events
Get-WinEvent -LogName "Microsoft-Windows-NTLM/Operational" -MaxEvents 200 -ErrorAction SilentlyContinue |
Where-Object { $_.Id -in @(8001, 8002, 8003, 8004) } |
Select-Object TimeCreated, Id,
@{N='EventType'; E={
switch ($_.Id) {
8001 { "NTLM client blocked audit" }
8002 { "NTLM server blocked audit" }
8003 { "NTLM server blocked in domain" }
8004 { "NTLM authentication to DC audit" }
}
}},
@{N='Detail'; E={$_.Message.Substring(0, [Math]::Min(300, $_.Message.Length))}} |
Format-Table -AutoSize
# Splunk: Monitor NTLM Audit Events (Event ID 8004)
# Shows all NTLM authentications passing through domain controllers
index=wineventlog source="WinEventLog:Microsoft-Windows-NTLM/Operational"
EventCode=8004
| rex field=Message "Calling client name:\s+(?<client_name>[^\r\n]+)"
| rex field=Message "Calling client IP:\s+(?<client_ip>[^\r\n]+)"
| rex field=Message "Server name:\s+(?<server_name>[^\r\n]+)"
| stats count dc(server_name) as unique_servers by client_name client_ip
| sort -count
| table client_name client_ip unique_servers count
步骤 7:PetitPotam 和强制认证攻击检测
# Splunk: Detect PetitPotam / EFSCoerce Attack
# Monitor for machine account NTLM authentications relayed to other services
index=wineventlog EventCode=4624 LogonType=3
AuthenticationPackageName="NTLM"
TargetUserName="*$"
| where match(TargetUserName, "^[A-Z0-9\\-]+\\$$")
| eval is_dc = if(match(TargetUserName, "(DC|DCSERVER|DOMCTRL)"), "Yes", "No")
| where IpAddress != "127.0.0.1" AND IpAddress != "::1"
| stats count values(ComputerName) as target_hosts
values(IpAddress) as source_ips by TargetUserName
| where count > 2 OR mvcount(source_ips) > 1
| table TargetUserName source_ips target_hosts count
| sort -count
-- Microsoft Sentinel KQL: PetitPotam / Coercion Attack Detection
-- Detects domain controller machine account authenticating from unexpected IPs
let dc_accounts = SecurityEvent
| where EventID == 4624 and LogonType == 3
| where TargetUserName endswith "$"
| where Computer startswith "DC"
| distinct TargetUserName;
SecurityEvent
| where EventID == 4624 and LogonType == 3
| where AuthenticationPackageName == "NTLM"
| where TargetUserName in (dc_accounts)
| where IpAddress != "127.0.0.1" and IpAddress != "::1"
| extend SourceHostExpected = iff(
Computer == replace_string(TargetUserName, "$", ""), true, false)
| where SourceHostExpected == false
| project TimeGenerated, Computer, TargetUserName, IpAddress,
WorkstationName, LogonProcessName, AuthenticationPackageName
| sort by TimeGenerated desc
# Sigma Rule: NTLM Relay - Computer Account Authentication from Unexpected Source
title: Potential NTLM Relay of Computer Account Credentials
id: 5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b
status: stable
description: >
Detects a computer account (ending in $) authenticating via NTLM LogonType 3
where the source IP does not match the computer's known IP, indicating possible
NTLM relay of coerced machine authentication (PetitPotam, DFSCoerce, PrinterBug).
references:
- https://www.crowdstrike.com/en-us/blog/how-to-detect-domain-controller-account-relay-attacks-with-crowdstrike-identity-protection/
- https://www.fox-it.com/nl-en/research-blog/detecting-and-hunting-for-the-petitpotam-ntlm-relay-attack/
- https://www.nccgroup.com/research-blog/detecting-and-hunting-for-the-petitpotam-ntlm-relay-attack/
logsource:
product: windows
service: security
detection:
selection:
EventID: 4624
LogonType: 3
AuthenticationPackageName: NTLM
TargetUserName|endswith: '$'
filter_localhost:
IpAddress:
- '127.0.0.1'
- '::1'
- '-'
condition: selection and not filter_localhost
level: high
tags:
- attack.credential_access
- attack.t1557.001
- attack.t1187
falsepositives:
- Legitimate NTLM authentication from machine accounts during failover
- Cluster service machine account authentication
步骤 8:构建综合关联仪表盘
# Splunk: NTLM Relay Detection Dashboard -- Combined Correlation Query
# Panel 1: IP-Hostname Mismatches (Core Relay Indicator)
index=wineventlog EventCode=4624 LogonType=3 AuthenticationPackageName="NTLM"
| where TargetUserName != "ANONYMOUS LOGON" AND NOT match(TargetUserName, ".*\\$$")
| eval mismatch=if(lower(WorkstationName) != lower(mvindex(split(IpAddress, "."), 0)),
"POSSIBLE_MISMATCH", "OK")
| where mismatch="POSSIBLE_MISMATCH"
| stats count by TargetUserName WorkstationName IpAddress ComputerName
# Panel 2: NTLMv1 Downgrade Events
index=wineventlog EventCode=4624 LmPackageName="NTLM V1"
| timechart span=1h count by ComputerName
# Panel 3: Machine Account Relay (PetitPotam Indicator)
index=wineventlog EventCode=4624 LogonType=3 AuthenticationPackageName="NTLM"
TargetUserName="*$"
| stats count values(IpAddress) as relay_sources by TargetUserName ComputerName
# Panel 4: NTLM Authentication Volume Anomaly
index=wineventlog EventCode=4624 LogonType=3 AuthenticationPackageName="NTLM"
| timechart span=15m count
| streamstats window=20 avg(count) as avg_count stdev(count) as stdev_count
| eval upper_bound=avg_count + (3 * stdev_count)
| where count > upper_bound
# Panel 5: SMB Signing Status (from audit results)
| inputlookup smb_signing_audit.csv
| stats count by Status
| table Status count
关键概念
| 术语 | 定义 |
|---|---|
| NTLM Relay (T1557.001) | 拦截 NTLM 认证消息并将其转发到目标服务的攻击,以受害者身份完成认证而无需知道其密码 |
| Event 4624 LogonType 3 | Windows 安全事件,表示成功的网络登录 —— 中继目标上生成的主要事件;源 IP 字段揭示了中继攻击者的地址 |
| IP-Hostname Mismatch | 当 Event 4624 的 WorkstationName 字段与 IpAddress 字段不对应时,表明认证是通过第三方中继的 |
| Responder | 攻击工具,通过投毒 LLMNR(UDP 5355)、NBT-NS(UDP 137)和 mDNS(UDP 5353)响应来捕获本地网络上受害者的 NTLM 认证 |
| ntlmrelayx | Fox-IT/Impacket 工具,将捕获的 NTLM 认证中继到 SMB、LDAP、HTTP、MSSQL 和其他协议以获取未授权访问 |
| SMB Signing | 对 SMB 数据包进行加密签名,防止针对 SMB 服务的中继攻击;必须设置为 "Required"(而不仅仅是 "Enabled")才能提供保护 |
| LDAP Signing | 对 LDAP 操作进行加密签名,防止针对域控制器上 LDAP 服务的中继攻击;由 LDAPServerIntegrity 注册表值控制 |
| LDAP Channel Binding | 扩展认证保护(EPA),将 NTLM 认证绑定到 TLS 通道,防止中继到 LDAPS |
| NTLMv1 Downgrade | 强制认证从 NTLMv2 降级到较弱的 NTLMv1 协议的攻击,该协议更容易被离线破解且中继保护较弱 |
| PetitPotam | 滥用 MS-EFSRPC 的强制认证技术,迫使域控制器向攻击者控制的主机进行认证,从而实现到 AD CS 或 LDAP 的中继 |
| LmCompatibilityLevel | 控制使用哪个 NTLM 版本的注册表设置;值为 5(仅发送 NTLMv2,拒绝 LM 和 NTLM)提供最强保护 |
| Event 8004 | 域控制器上的 NTLM 操作日志事件,显示所有 NTLM 认证传递,对于在限制之前审计 NTLM 使用至关重要 |
工具和系统
| 工具 | 用途 |
|---|---|
| Splunk / Elastic SIEM | 日志聚合和关联,用于 Event 4624 分析、IP-主机名不匹配检测和 NTLM 降级监控 |
| Microsoft Sentinel | 云 SIEM,具有用于 NTLM 中继检测的 KQL 查询和针对 PetitPotam 的内置分析规则 |
| CrowdStrike Falcon Identity Protection | 检测针对域控制器账户的 NTLM 中继攻击,无论使用何种强制认证方法 |
| Responder | 攻击者使用的 LLMNR/NBT-NS/mDNS 投毒工具 —— 了解其行为对于检测至关重要 |
| ntlmrelayx (Impacket) | 由 Fox-IT 开发的多协议 NTLM 中继工具 —— 用于测试和攻击方 |
| PingCastle | Active Directory 安全评估工具,审计 SMB 签名、LDAP 签名和 NTLM 配置 |
| Zeek | 网络安全监控器,用于捕获 SMB 签名协商、LLMNR 流量和 DCE-RPC 活动 |
| Sigma | 供应商无关的检测规则格式,用于可移植的 NTLM 中继检测规则 |
常见场景
场景 1:通过 NTLM 中继到文件服务器的响应者投毒
背景:SOC 分析师在文件服务器(10.10.20.100)上观察到多条 Event 4624 LogonType 3 记录,其中 WorkstationName 字段显示不同的工作站名称,但 IpAddress 字段始终显示 10.10.5.50,该主机不在 IT 资产清单中。
方法:
- 在 10.10.20.100 上查询 Event 4624,过滤条件为 IpAddress=10.10.5.50:发现在 30 分钟内来自 8 个不同用户账户的 15 次成功 NTLM 登录
- 将 10.10.5.50 与 DHCP 日志和 DNS 进行交叉比对:该主机不是已注册的域成员,MAC 地址显示为基于 Linux 的网卡
- 在 Zeek 网络日志中查询 10.10.5.50:识别出对多个工作站的 LLMNR 响应(UDP 5355)以及到 10.10.20.100 的 SMB 连接
- 确认 IP 与主机名不匹配:WorkstationName 值(WS-FINANCE01、WS-HR03 等)在 DNS 中均解析到不同的 IP,而非 10.10.5.50
- 检查 10.10.20.100 上的 SMB 签名:RequireSecuritySignature 为 False,从而使中继攻击成为可能
- 遏制:在交换机上封锁 10.10.5.50,强制重置所有 8 个受影响账户的密码,在文件服务器上启用 SMB 签名
- 修复:通过 GPO 禁用 LLMNR 和 NBT-NS,在整个域范围内强制实施 SMB 签名
陷阱:
- 未检查 IP 与主机名的关联,就将多次登录视为正常网络活动而忽略
- 未检查目标服务器上的 SMB 签名状态,以了解中继为何成功
- 仅重置一个用户的密码,而不是重置所有被中继的账户
场景 2:PetitPotam 中继到 AD 证书服务
背景:在一次威胁狩猎中,分析师在 AD CS 服务器(ADCS01)上发现 Event 4624 LogonType 3 记录,显示域控制器机器账户(DC01$)从 IP 10.10.5.50 通过 NTLM 进行身份验证,而该 IP 并非 DC 的 IP 地址(10.10.1.10)。
方法:
- 确认异常:DC01$ 应仅从 10.10.1.10 进行身份验证,但 Event 4624 显示其从 10.10.5.50 通过 NTLM(而非 Kerberos)进行身份验证
- 检查证书注册:在同一时间戳前后查询 AD CS 日志中来自 DC01$ 的证书请求——发现为 DC01$ 颁发了一张证书
- 识别攻击:PetitPotam 强制 DC01 向 10.10.5.50 进行身份验证,后者将该身份验证中继到 ADCS01,以请求为 DC01$ 颁发证书
- 评估影响:凭借 DC 证书,攻击者可以 DC01$ 身份进行身份验证并执行 DCSync,以提取所有域凭据
- 立即吊销被欺诈性颁发的证书
- 检查 DCSync 活动:查询 Event 4662 中来自非 DC 来源的目录复制
- 遏制:隔离 10.10.5.50,吊销证书,修补 EFS(MS-EFSRPC),在 AD CS 上强制实施 EPA,要求所有 DC 上启用 LDAP 签名
陷阱:
- 未认识到来自意外 IP 的机器账户 NTLM 身份验证是强制认证 + 中继的关键指标
- 未能检查 AD CS 是否存在欺诈性证书颁发,而这正是攻击的实际目标
- 未审计 AD CS 服务器上的 LDAP 签名和 EPA,而这些本可阻止中继
输出格式
Hunt ID: TH-NTLM-RELAY-[DATE]-[SEQ]
Alert Severity: Critical
MITRE Technique: T1557.001 (LLMNR/NBT-NS Poisoning and SMB Relay)
Relay Indicators:
Victim Account: [Domain\Username or Machine$]
WorkstationName: [Victim hostname from Event 4624]
Expected Source IP: [IP matching WorkstationName in DNS/DHCP]
Actual Source IP: [Attacker/relay IP from Event 4624 IpAddress field]
Target Host: [Server receiving the relayed authentication]
Authentication Details:
Event ID: 4624
LogonType: 3 (Network)
AuthenticationPackage: NTLM
LmPackageName: [NTLM V1 or NTLM V2]
LogonProcess: [NtLmSsp]
Timestamp: [Event time]
Signing Status:
Target SMB Signing: [Required/Not Required]
Target LDAP Signing: [Required/Not Required]
LDAP Channel Binding: [Required/Not Required]
Poisoning Evidence:
LLMNR Activity: [Detected/Not Detected from relay IP]
NBT-NS Activity: [Detected/Not Detected from relay IP]
Coercion Method: [PetitPotam/DFSCoerce/PrinterBug/Unknown]
Risk Assessment: [Critical - relay from DC / High - relay from user account]
Recommended Actions:
- Immediate: [Block relay IP, reset affected credentials]
- Short-term: [Enable SMB/LDAP signing, disable LLMNR/NBT-NS]
- Long-term: [Migrate to Kerberos, enforce EPA, restrict NTLM via GPO]