Use PowerShell to Block IP Addresses (Fail2ban)

This blog provides an example of how to use PowerShell to block IP addresses (Fail2ban) from gaining access to a Windows server.

Use PowerShell to Block IP Addresses (Fail2ban)

Web can servers constantly get brute force attacks from unscrupulous people (or bots) trying to gain access via RDP and SQL. Similar to Fail2ban (loosely), I wrote a quick script to scan the event logs for unsuccessful login attempts for RDP and SQL. If there is more than 10 failed attempts per IP address on a single day (you can change this limit) it will be added to a firewall rule to block remote (incoming) access.

I run this on a scheduled task once per day.

#run this as a scheduled task once per day
#log file path
$logFile = "C:\AppSource\Blacklist.log"
#firewall rule name
$firewallRuleName = "BlockBruteForceLoginAttempts"
#if more than the following failed login attempts per day from the same IP, blacklist the IP
$loginFailedDailyAttemptsLimit = 10
function Write-Log {
[CmdletBinding()]
param(
[string]$message
)
try {
Add-Content -Value "$((Get-Date).tostring('dd/MM/yyyy HH:mm:ss')) - $message" -Path $logFile
} catch {}
}
Write-Log "Started"
$firewallRule = Get-NetFirewallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
if ($firewallRule -eq $null) {
#mustadd a default address to block, otherwise could block your own connection!
$firewallRule = New-NetFirewallRule -DisplayName $firewallRuleName -Direction Inbound -Action Block -RemoteAddress 1.1.1.1
Write-Log "Created firewall rule called $firewallRuleName"
}
if ($firewallRule -ne $null) {
$today = $(get-date).date;
$yesterday = $($today).AddDays(-1);
#init to emty array
$allFilteredBlacklistedIPs = @()   
#find all failed SQL logins in the last day
$AllFailedRDPLogins = get-eventlog -LogName Security -After $yesterday `
| Where-Object { $_.eventid -eq 4625 } `
| Where-Object {$_.Message -match "Logon Type:\s+(3)\s" -and $_.ReplacementStrings[-2] -ne "-"} `
| Select-Object -Property @{Name = 'Client'; Expression = {$_.ReplacementStrings[-2].trim()}}
#get count of failed login attempts per client/IP and blacklist if more than theshold
$filteredFailedRDPLogins = $AllFailedRDPLogins | Select-Object -ExpandProperty Client | Group-Object | Select Name, Count | where-object Count -gt $loginFailedDailyAttemptsLimit 
foreach ($failedLogin in $filteredFailedRDPLogins) {
Write-Log "Blacklist RDP Login IP $($failedLogin.Name) due to $($failedLogin.Count) failed login attempts in the last 24 hours"
$allFilteredBlacklistedIPs += $failedLogin.Name
}  
#find all failed SQL logins in the last day
$AllFailedSQLLogins = get-eventlog -LogName Application -After $yesterday `
| Where-Object { $_.eventid -eq 18456 } `
| Where-Object {$_.Message -match "user '(?<login>.+)'.+Reason:.(?<reason>.+).+\[CLIENT:(?<client>.+)\]" } `
| Select-Object -Property @{Name = 'Client'; Expression = {$matches['client'].trim()}}
#get count of failed login attempts per client/IP and blacklist if more than theshold
$filteredFailedSQLLogins = $AllFailedSQLLogins | Select-Object -ExpandProperty Client | Group-Object | Select Name, Count | where-object Count -gt $loginFailedDailyAttemptsLimit 
foreach ($failedLogin in $filteredFailedSQLLogins) {
Write-Log "Blacklist SQL Login IP $($failedLogin.Name) due to $($failedLogin.Count) failed login attempts in the last 24 hours"
$allFilteredBlacklistedIPs += $failedLogin.Name
}
#get existing blacklisted IPs
$existingBlacklistedIPs = ($firewallRule | Get-NetFirewallAddressFilter).RemoteAddress
#add existing blacklisted IPs to newly blacklisted IPs
$allBlacklistedIPs = $allFilteredBlacklistedIPs + $existingBlacklistedIPs
#add to rule
Set-NetFirewallRule -DisplayName $firewallRuleName -RemoteAddress $allBlacklistedIPs
}
Write-Log "Finished"