In my previous company Exchange OWA isn‘t published to internet, so this blog described my first time encountering hacker trying to hack my new company‘s Active directory passwords. Based on it, I wrote a powershell script running on each CAS servers to use 4625 events identify hacker‘s source IP and block it.
Environment is Windows 2012 and Exchange 2013
First of all, some points need to be noticed,
Normally, there are more than 1 CAS servers in production Exchange environment, so the script must be setup on each of them.
Event ID is 4625, its message contains source IP address and account names.
You probably need to enable auditing first, otherwise probably no 4625 will be logged.
Here is the procedure,
After the script triggered, it will capture events in Security logs for past 10 minutes, filter out all 4625 events; each by each, script analysis XML data of event and gets source IP and target AD account; use IP as the "key", AD accounts as the "Value", store data in a hash table; at last, script loop every IP address in hash table and block IP address which exceed defined threshold and send warning email to admins.
#Enter script‘s parent directory Set-Location (Get-Item ($MyInvocation.MyCommand.Definition)).DirectoryName #The time script should back to trace logs $MinutesToBack = 10 $Date = Get-Date $strDate = $Date.ToString(‘yyyy-MM-dd‘) $End_time = $Date $Start_time = $Date.AddMinutes(-$MinutesToBack) #Two log files, script only send logs for past 10 minutes content $strLogFile = "${strDate}.txt" $strLogFile_e = "${strDate}_e.txt" Set-Content -Path $strLogFile_e -Value $null #从FW_WhiteList.txt is the IP whitelist, there always some exceptions $WhiteList = @(Get-Content -Path ‘FW_WhiteList.txt‘ -ErrorAction:SilentlyContinue)
#Define threshold to add into firewall, 50 means total failure authencations, 10 means AD accounts de-duplicated, if one IP address exceeds both values, it will be in firewall (except those in whitelist) $t_4625_fw = @(50, 10)
#email sending part $Mail_From = "$($env:COMPUTERNAME)@xxxx.yyyy" $Mail_To = ‘[email protected]‘, ‘[email protected]‘ $Mail_Subject = ‘warning email subject‘ $Mail_SMTPServer = ‘SMTP server address‘
The script is based on 4625 events, so i just hardcoded 4625 in script
PS: Why use get-winevent here? cause it has a method to get XML data of one event, i don‘t need to care about locale/language, etc.
#logging some information Add-Log -Path $strLogFile_e -Value "Catch logs after : $($Start_time.ToString(‘HH:mm:ss‘))" -Type Info Add-Log -Path $strLogFile_e -Value "Catch logs before: $($End_time.ToString(‘HH:mm:ss‘))" -Type Info #get logs from past defined minutes $4625 = @(Get-WinEvent -FilterHashtable @{LogName = ‘Security‘; Id = 4625; StartTime = $Start_time; EndTime = $End_time;} -ErrorAction:SilentlyContinue) #output the number of 4625 Add-Log -Path $strLogFile_e -Value "Total 4625 logs count : [$($4625.Count)]" -Type Info
loop each event, convert to XML data and get IP and AD account name, store in hashtable,
# http://schemas.microsoft.com/win/2004/08/events/event # index 5 = TargetUserName # index 19 = IpAddress $s_4625 = @{} foreach($e in $4625) { $xmlData = $IP = $Account = $null $xmlData = [xml]$e.ToXml() $IP = $( if($xmlData.Event.EventData.Data[19].‘#text‘ -imatch ‘^\s*$‘) { ‘NULL‘ } else { $xmlData.Event.EventData.Data[19].‘#text‘.Trim() } ) $Account = $( if($xmlData.Event.EventData.Data[5].‘#text‘ -imatch ‘^\s*$‘) { ‘NULL‘ } else { $xmlData.Event.EventData.Data[5].‘#text‘.Trim() } ) $s_4625.$($IP) += @($Account) }
loop each IP in hashtable, compare with predefined threshold, adding to firewall or do some other actions.
foreach($IP in $s_4625.Keys) { $tmp = @($s_4625.$IP | Group-Object | Sort-Object Count -Descending) Add-Log -Path $strLogFile_e -Value "In past [${MinutesToBack}] minutes [IP][Total][AD Accounts][Top 5]:[$IP][$($s_4625.$IP.Count)][$($tmp.Count)][$($tmp[0..4] | %{$_.Name, $_.Count -join ‘:‘})]" -Type Info if($s_4625.$IP.Count -ge $t_4625_fw[0] -and $tmp.Count -ge $t_4625_fw[1]) { $tmp.Name | Add-Content -Path "$IP.log" -Encoding Default if($WhiteList -notcontains $IP) { $Mail = $true New-NetFirewallRule -DisplayName "ScriptAuto_$IP" -Profile Any -Action Block -RemoteAddress $IP -Direction Inbound -ErrorAction:SilentlyContinue if(!$?) { Add-Log -Path $strLogFile_e -Value ‘Adding to firewall failed,cause:‘ -Type Error Add-Log -Path $strLogFile_e -Value $Error[0] -Type Error } else { Add-Log -Path $strLogFile_e -Value "[$IP] added to firewall" -Type Warning } } else { Add-Log -Path $strLogFile_e -Value "[$IP] is in whitelist" -Type Info } } else { Add-Log -Path $strLogFile_e -Value "[$IP] not exceed threshold" -Type Info } }
Send out email notification if necessary,
If($Mail) { try { Send-MailMessage -From $Mail_From -To $Mail_To -Subject $Mail_Subject -SmtpServer $Mail_SMTPServer -Body ((Get-Content $strLogFile_e -Encoding Default) -join "`t`n") -Encoding utf8 } catch { Add-Log -Path $strLogFile_e -Value "Failed to send mail, cause: $($Error[0])" -Type Error } } Get-Content -Path $strLogFile_e | Add-Content -Path $strLogFile Add-Log -Path $strLogFile_e -Value ‘Completed‘ -Type Info
Triggered by task scheduler every 10 minutes, based on your environment and exprience define the threshould, it works.