This artical will also be published in English: http://www.cnblogs.com/LarryAtCNBlog/p/4820327.html
以前所在的公司OWA什么的都不对外开放倒是从来没有经历过利用Exchange OWA的AD账户密码爆破行为,入职了一个新的公司后OWA对公网开放使用,结果引来了不少的攻击。于是乎做了一个powershell的脚本,数据源基于Exchange CAS上的security日志,用计划任务每10分钟检查一次,超过阀值的则扔进CAS的防火墙block掉。
环境是Windows 2012 / Exchange 2013
另外,需要注意如下几点,
首先CAS服务器一般都不止一台,而且做了NLB,因此脚本要放在每台CAS上都设置执行,
其次验证错误的日志ID号为4625,Message里包括了详细信息如源IP,账户名什么的,
再次CAS服务器上需要开启日志审核,否则可能没有该日志被记录下来。
流程先描述一下,
脚本运行后从Security日志中抓取过去10分钟的条目,过滤出所有4625的条目;逐条对每条4625日志循环,从日志的XML数据中提取出被验证的账户和验证源IP,以IP为key,账户为值,记录于哈希表中;最后对哈希表中的的IP做循环将超过阀值的IP加入防火墙block掉,发出邮件告警。
#进入脚本所在目录 Set-Location (Get-Item ($MyInvocation.MyCommand.Definition)).DirectoryName #要追踪多长时间之前的日志,和计划任务也要对应好 $MinutesToBack = 10 $Date = Get-Date $strDate = $Date.ToString(‘yyyy-MM-dd‘) $End_time = $Date $Start_time = $Date.AddMinutes(-$MinutesToBack) #定义两个日志文件,邮件告警时仅会发送过去10分钟的日志内容 $strLogFile = "${strDate}.txt" $strLogFile_e = "${strDate}_e.txt" Set-Content -Path $strLogFile_e -Value $null #从FW_WhiteList.txt读取白名单的IP地址,总归会有些例外,例外的IP地址以每行的形式写在该文本中 $WhiteList = @(Get-Content -Path ‘FW_WhiteList.txt‘ -ErrorAction:SilentlyContinue)
#定义加入防火墙的阀值,50代表验证的密码错误次数,10代表账号去掉重复之后的账户数目,两个值都被超过就会被加入防火墙,白名单中的例外 $t_4625_fw = @(50, 10)
#没什么好说的,邮件告警部分 $Mail_From = "$($env:COMPUTERNAME)@xxxx.yyyy" $Mail_To = ‘[email protected]‘, ‘[email protected]xxxx.yyyy‘ $Mail_Subject = ‘邮件标题‘ $Mail_SMTPServer = ‘SMTP地址‘
由于日志是直接以4625来判断,我也就直接把4625直接写死在了脚本中。
这里为会什么要用Get-WinEvent呢?因为它有一个方法获取日志的xml格式数据,处理起来有通用的方法,不用管地区语言之类的,
#日志信息 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 #从Eventlog中抓过去一段时间的日志 $4625 = @(Get-WinEvent -FilterHashtable @{LogName = ‘Security‘; Id = 4625; StartTime = $Start_time; EndTime = $End_time;} -ErrorAction:SilentlyContinue) #输出4625日志数目 Add-Log -Path $strLogFile_e -Value "Total 4625 logs count : [$($4625.Count)]" -Type Info
对每条日志循环处理,转换为xml后取出IP和账户名存在hash table中,
# 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) }
将Hash table中的key也就是IP做循环,以之前定义的阀值相对比后做加入防火墙的操作,
foreach($IP in $s_4625.Keys) { $tmp = @($s_4625.$IP | Group-Object | Sort-Object Count -Descending) Add-Log -Path $strLogFile_e -Value "过去[${MinutesToBack}]分钟[IP地址][错误量][账户][前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 ‘加入firewall失败,原因:‘ -Type Error Add-Log -Path $strLogFile_e -Value $Error[0] -Type Error } else { Add-Log -Path $strLogFile_e -Value "[$IP]加入防火墙" -Type Warning } } else { Add-Log -Path $strLogFile_e -Value "[$IP]在白名单中" -Type Info } } else { Add-Log -Path $strLogFile_e -Value "[$IP]未超过阀值" -Type Info } }
最后如果有必要发出邮件的话就通过smtp发出,
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
计划任务中设置每10分钟跑一次,根据自己环境和经验设置好阀值,最近想通过OWA爆破密码的IP也是各种被封掉,效果还是明显的。