How to audit NTLM?

I’ve seen the following blog post last week about Stop using LAN Manager and NTLMv1!

The first step proposed that sounds reasonable and wise is to audit event ID 4624.
Here’s one way to do it:

$c =  Get-Credential 
$xml = @'
 <Query Id="0" Path="security">
  <Select Path="security">
     *[EventData[Data[@Name='LmPackageName']!='NTLM V2']]
(Get-ADDomainController  -Filter *).HostName | 
Where { $_ -notin @('dc1.fqdn','dc3.fqdn') } | 
ForEach-Object {
 Get-WinEvent -ComputerName $_ -FilterXml $xml  -ErrorAction SilentlyContinue -Credential $c | # -MaxEvents 1
 ForEach-Object {
  $h = @{}
  ([xml]$_.Toxml()).Event.EventData.Data | 
  ForEach-Object {
} | Out-GridView

The above example shows how to audit 4624 events on domain controllers but you can also audit 4624 events on any computer.

What else could be done to audit NTLM?
You can also enable specific NTLM auditing on every computer using group policies.
By default, it’s empty and off.

But once you’ve activated these 3 settings:

You can also activate these settings using the following registry modifications:

# Audit NTLM Authentication in this domain: Enable all
$HT = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\services\Netlogon\Parameters' }
Set-ItemProperty @HT -Name AuditNTLMInDomain -Value 7

# Audit incoming NTLM traffic: Enable auditing for all accounts
$HT = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' }
Set-ItemProperty @HT -Name AuditReceivingNTLMTraffic -Value 2

# Restrict NTLM: Outgoing NTLM traffic to remote servers: Audit All
$HT = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' }
Set-ItemProperty @HT -Name RestrictSendingNTLMTraffic -Value 1

To list what events will be logged you can do:

(Get-WinEvent -ListProvider Microsoft-Windows-NTLM).Events | 
Select Id,Description | 

When auditing is enabled, we should look at 8001,8002 and 8003 events.
Here’s how to have a quick overview of both 8001 and 8002 events combined:

# NTLM client blocked audit: 
# Audit outgoing NTLM authentication traffic that would be blocked.
Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-NTLM/Operational' ; Id = 8001,8002 } |
ForEach-Object {
 $e = $_
 switch ($e.Id) {
  8001 {
   $Direction = 'Out'
   $TargetName = $e.Properties[0].Value ;
   $ProcessID = $e.Properties[3].Value 
   $ProcessName = $e.Properties[4].Value ;
   $Identity =  "$($e.Properties[2].Value)\$($e.Properties[1].Value)"
  8002 {
   $Direction = 'In'
   $TargetName = $env:COMPUTERNAME
   $ProcessID = $e.Properties[0].Value 
   $ProcessName = $e.Properties[1].Value ;
   $Identity =  "$($e.Properties[4].Value)\$($e.Properties[3].Value)"
  default {}
  TargetName = $TargetName
  Direction = $Direction
  ProcessId = $ProcessID
  ProcessName  = $ProcessName
  Identity = $Identity
} | Out-GridView

Remoting error 0x80090311

Last week, I had to enable someone in a domain to restart computers in a another domain.
I’ve first created a restricted endpoint on the Domain Controller itself.
I could enter the endpoint and use the only cmdlet exposed (Restart-Computer) with its limited parameters and values.
But, when I tried to use the endpoint from the computer in the other domain using valid credentials I had the following error:

WARNING: Failed to create and import session because Connecting to remote server server.fqdn failed with the following error message : WinRM cannot process the request. The following error with errorcode 0x80090311 occurred while using Kerberos authentication: There are currently no logon servers available to service the logon request.
Possible causes are:

How can I have a no logon servers available for authentication with the DC itself?
To make it work, I only changed the way I entered credentials.
I initially typed

-Credential (Get-Credential NetBiosDomainName\UserName)

and replaced it with

-Credential (Get-Credential FullyQualifiedDomainName\UserName)

Fine but afterward I encountered the issue I reported a few weeks ago on this blog post D’oh!
Running the Get-Command command in a remote session reported the following error: A parameter cannot be found that matches parameter name ‘PowerShellVersion’

No problem, it’s easier to move the endpoint to a server that runs PowerShell 4.0 than to remove PowerShell 5.1 from client computers.

Again with this other server, I had the same error 0x80090311: no logon servers available for Kerberos authentication 😦
This time, I had to modify the target computer name where the endpoint is located and write it using the correct case that matches the way SPN (Service Principal Name) are identified in Active Directory.
The Test-WSMan cmdlet behaved the same way:

Test-WSMan -ComputerName TargetserverFqDNincorretcase -Authentication Kerberos -Credential (Get-Credential FullyQualifiedDomainName\UserName)

and replaced it with

Test-WSMan -ComputerName TargetServerFQDNCorretCase -Authentication Kerberos -Credential (Get-Credential FullyQualifiedDomainName\UserName)

Now that the authentication worked, I hit another unexpected road block.
Using domain admin credentials I could do on the DC:

 Enter-PSSession -ComputerName localhost -ConfigurationName 'MySecureEndPoint'

But not on the member server. I got the following error: Enter-PSSession : AuthorizationManager check failed.

Well, I only found one configuration item that was inconsistent with the way my endpoint is configured that could explain the fact I get the equivalent of an “access denied”.
I’m using a RunAs account who is able to restart remote computers and both the DC and the member server have this policy set:

After removing the whole GPO that configured WinRM, I was finally able to deliver the reboot button:

Achievement unlocked 😉