Wrapper - phbits/WebsiteFailedLogins GitHub Wiki
Using this technique, the wrapper script is launched via Scheduled Tasks which then runs Invoke-WebsiteFailedLogins
. The returned object can then be processed as desired.
The following PowerShell script can be used as a wrapper. Update the "User Defined Variables" and include custom code to achieve the desired action when results are found.
Import-Module WebsiteFailedLogins
<#
## User Defined Variables ##
#>
# Number of seconds an IP should be blocked/limited/etc.
[Int] $MaxSeconds = 3600
# Path to ClientIp persistence file
# If no file exists it will be created
[String] $ClientIpPersistenceFile = 'D:\WebsiteFailedLogins\W3SVC1-blocked-ips.xml'
# Path to configuration file
[String] $WebsiteFailedLoginsIni = 'D:\WebsiteFailedLogins\W3SVC1.ini'
<#
## Start Script ##
#>
# Hashtable to hold all ClientIP currently being blocked/limited/etc.
[Hashtable] $ClientIpList = @{}
if (Test-Path -LiteralPath $ClientIpPersistenceFile)
{
$ClientIpList = Import-Clixml -LiteralPath $ClientIpPersistenceFile
}
# IIS logs in UTC
$ExpireTimeStamp = (Get-Date).ToUniversalTime().AddSeconds($(-1 * $MaxSeconds))
$Results = Invoke-WebsiteFailedLogins -RunningConfig -Configuration $WebsiteFailedLoginsIni
if ($Results.HasError -eq $true)
{
# process error
# Send-MailMessage -Body $($Results.ErrorMessages -join [System.Environment]::NewLine)
}
if ($Results.HasResults -eq $true)
{
if ($Results.FailedLoginsPerIP.Count -gt 0)
{
foreach ($key in $Results.FailedLoginsPerIP.Keys)
{
if ($ClientIpList.ContainsKey($key) -eq $false)
{
# Example Hashtable data:
#
# $Results.FailedLoginsPerIP[$key].ClientIP # 10.1.1.10
# $Results.FailedLoginsPerIP[$key].FailedLogins # 30
# $Results.FailedLoginsPerIP[$key].Sitename # W3SVC1
# $Results.FailedLoginsPerIP[$key].IISLogPath # D:\logs\W3SVC1
# $Results.FailedLoginsPerIP[$key].Authentication # Forms
# $Results.FailedLoginsPerIP[$key].HttpResponse # 401
# $Results.FailedLoginsPerIP[$key].UrlPath # /login.aspx
# $Results.FailedLoginsPerIP[$key].Start # 2021-01-26T18:02:51Z
# $Results.FailedLoginsPerIP[$key].'End~' # 2021-01-26T18:22:51Z
# <insert custom code here>
# add IP to list
$ClientIpList.Add($key, $Results.FailedLoginsPerIP[$key].'End~')
} else {
# IP is already blocked. This is expected and a result of
# the recommended configuration. It occurs when StartTime
# is much larger than how frequently the script is launched
# which causes the same logs to be included in WebsiteFailedLogins
# analysis for multiple iterations.
}
}
}
if ($Results.TotalFailedLogins.ContainsKey('TotalFailedLogins') -eq $true)
{
# Example Hashtable data:
#
# $Results.TotalFailedLogins.TotalFailedLogins # 100
# $Results.TotalFailedLogins.ClientIpList # hashtable
# $Results.TotalFailedLogins.ClientIpList['1.1.1.1'] # 90
# $Results.TotalFailedLogins.Sitename # W3SVC1
# $Results.TotalFailedLogins.IISLogPath # D:\logs\W3SVC1
# $Results.TotalFailedLogins.Authentication # Forms
# $Results.TotalFailedLogins.HttpResponse # 401
# $Results.TotalFailedLogins.UrlPath # /login.aspx
# $Results.TotalFailedLogins.Start # 2021-01-26T18:02:51Z
# $Results.TotalFailedLogins.'End~' # 2021-01-26T18:22:51Z
# <insert custom code here>
}
}
# Process ClientIP list being blocked/limited/etc.
foreach ($key in $ClientIpList.Keys)
{
$alertEnd = [System.DateTime]::Parse($ClientIpList[$key])
if ($alertEnd -lt $ExpireTimeStamp)
{
# allow ip previously blocked/limited/etc.
# remove IP from firewall rule, config, etc.
# <insert custom code here>
# remove IP from list
$ClientIpList.Remove($key)
}
}
# Save updated ClientIP list to persistence file
$ClientIpList | Export-Clixml -LiteralPath $ClientIpPersistenceFile -Force