Menggunakan skrip remunda yang bagus sebagai titik awal, saya menambahkan satu hal utama yang hilang: memblokir alamat IP dari login FTP yang gagal . Windows Server tidak mencatat alamat IP ke log Keamanan ketika seseorang gagal masuk melalui FTP, tetapi sebaliknya menetapkan "Alamat Jaringan Sumber" ke tanda hubung. FTP adalah vektor serangan yang sangat umum untuk serangan brute force, jadi saya menambahkan pada skripnya kemampuan untuk memindai log FTP hari ini untuk beberapa kegagalan login dan memblokir alamat IP tersebut juga.
Pembaruan 2014/02/07: Ketika saya membuat beberapa tweak untuk memproses semua log FTP lama saya, saya menyadari ketika mereka memiliki sejumlah besar upaya (50.000+), array yang dibuatnya akan sangat besar dan membuat pemrosesan menjadi sangat lambat. Sejak itu saya menulis ulang untuk membuatnya jauh lebih efisien ketika memproses log FTP.
Saya juga menemukan bahwa ada batas keras sewenang-wenang 1000 untuk berapa banyak IP dapat dalam satu aturan Windows Firewall. Karena batas itu, saya memerlukannya untuk secara otomatis membuat aturan baru ketika yang terbaru terisi. Sekarang melakukan hal itu, dan juga membuat aturan firewall awal (jika Anda tidak membuat sendiri) sehingga satu-satunya pengaturan yang harus dilakukan adalah menambahkannya ke Penjadwal untuk dijalankan ketika ada peristiwa 4625.
Berikut kode, yang telah diuji pada Windows Server 2008 R2 dan Windows 7:
# This Windows Powershell script will automatically block IP addresses that attempt to login to the system
# and fail the number of times set below with the $int_block_limit variable or more. Is scans both the Security
# log, which covers Remote Desktop and other attempts, as well as the current day's FTP log. If the $int_block_limit
# limit is hit on either of those logs (separately, not combined), then the IP address will be added to the
# firewall rule.
#
# The script will automatically create a firewall rule named "BlockAttackers (Created yyyy-MM-dd HH:mm:ss UTC)" using
# the current time if one with a name that includes "BlockAttackers" doesn't already exist. Because there's a hard
# limit of 1000 entries (IP addresses) you can block per rule, it will also create similarly-named rules once that
# limit is reached for the latest one.
#
# I recommend setting the script to run as a scheduled task triggered by event 4625 login audit failures from the
# Security log, or alternatively you could set it to run after some amount of time (i.e. every 10 minutes).
#
# Authors:
# Majority of script written by serverfault.com user kevinmicke
# Windows Security Log portion written by serverfault.com user remunda, which provided the starting point for kevinmicke
#
# Details: https://serverfault.com/questions/233222/ban-ip-address-based-on-x-number-of-unsuccessful-login-attempts
# Set number of failed login attempts after which an IP address will be blocked
$int_block_limit = 10
# Time window during which to check the Security log, which is currently set to check only the last 24 hours
$dat_time_window = [DateTime]::Now.AddDays(-1)
# Select from the Security log all IP addresses that have more than $int_block_limit audit failures (event 4625) within $dat_time_window
$arr_new_bad_ips_security_log = @()
$arr_new_bad_ips_security_log = Get-EventLog -LogName 'Security' -InstanceId 4625 -After $dat_time_window |
Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]}} |
Group-Object -property IpAddress |
Where {$_.Count -ge $int_block_limit} |
Select -property Name
# Get current time UTC to figure out filename for current FTP log
$current_date_utc = (Get-Date).ToUniversalTime()
# Set path to today's FTP log file
$str_log_file_name = "C:\inetpub\logs\LogFiles\FTPSVC2\u_ex" + $current_date_utc.ToString("yyMMdd") + ".log"
# Search today's FTP log file for "530 1326" to find lines that contain IPs of systems that failed to log in,
# get just the IP from each line, group the IPs by IP to count the attempts from each one, and select only the
# IPs that have $int_block_limit or more bad logins today
$arr_new_bad_ips_ftp = @()
$arr_new_bad_ips_ftp = Select-String $str_log_file_name -pattern "530 1326" |
ForEach-Object {$_.Line.Substring(20,15) -replace " .*", ""} |
Group |
Where {$_.Count -ge $int_block_limit} |
Select -property Name
# Concatenate the two arrays of IPs (one from Security log, one from FTP log)
$arr_new_bad_ips_all = @()
# $arr_new_bad_ips_all = @($arr_new_bad_ips_security_log) + @($arr_new_bad_ips_ftp_over_limit)
$arr_new_bad_ips_all = @($arr_new_bad_ips_security_log) + @($arr_new_bad_ips_ftp)
# Sort the array, selecting only unique IPs (in case one IP shows up in both the Security and FTP logs)
$arr_new_bad_ips_all_sorted = @()
$arr_new_bad_ips_all_sorted = $arr_new_bad_ips_all |
Foreach-Object { [string]$_.Name } |
Select-Object -unique
# Get firewall object
$firewall = New-Object -comobject hnetcfg.fwpolicy2
# Get all firewall rules matching "BlockAttackers*"
$arr_firewall_rules = $firewall.Rules | Where {$_.Name -like 'BlockAttackers*'}
# If no "BlockAttackers*" firewall rule exists yet, create one and set it to a variable
if ($arr_firewall_rules -eq $null) {
$str_new_rule_name = "BlockAttackers (Created " + $current_date_utc.ToString("yyyy-MM-dd HH:mm:ss") + " UTC)"
netsh advfirewall firewall add rule dir=in action=block name=$str_new_rule_name description="Rule automatically created by BlockAttackers Powershell script written by Kevin Micke." enable=yes remoteip="0.0.0.0" | Out-Null
$arr_firewall_rules = $firewall.Rules | Where {$_.Name -like 'BlockAttackers*'}
}
# Split the existing IPs from current "BlockAttackers*" firewall rule(s) into an array so we can easily search them
$arr_existing_bad_ips = @()
foreach ($rule in $arr_firewall_rules) {
$arr_existing_bad_ips += $rule.RemoteAddresses -split(',')
}
# Clean subnet masks off of IPs that are currently blocked by the firewall rule(s)
$arr_existing_bad_ips_without_masks = @()
$arr_existing_bad_ips_without_masks = $arr_existing_bad_ips | ForEach-Object {$_ -replace "/.*", ""}
# Select IP addresses to add to the firewall, but only ones that...
$arr_new_bad_ips_for_firewall = @()
$arr_new_bad_ips_for_firewall = $arr_new_bad_ips_all_sorted | Where {
# contain an IP address (i.e. aren't blank or a dash, which the Security log has for systems that failed FTP logins)
$_.Length -gt 6 -and
# aren't already in the firewall rule(s)
!($arr_existing_bad_ips_without_masks -contains $_) -and
# aren't the local loopback
!($_.StartsWith('127.0.0.1')) -and
# aren't part of the local subnet
!($_.StartsWith('192.168.')) -and
!($_.StartsWith('10.0.'))
}
# If there are IPs to block, do the following...
if ($arr_new_bad_ips_for_firewall -ne $null) {
# Write date and time to script-specific log file
[DateTime]::Now | Out-File -Append -Encoding utf8 C:\blockattackers.txt
# Write newly-blocked IP addresses to log file
$arr_new_bad_ips_for_firewall | Out-File -Append -Encoding utf8 C:\blockattackers.txt
# Boolean to make sure the new IPs are only added on one rule
$bln_added_to_rule = 0
# Array to hold bad IPs from each rule one at a time, so we can count to make sure adding the new ones won't exceed 1000 IPs
$arr_existing_bad_ips_current_rule = @()
# For each "BlockAttackers*" rule in the firewall, do the following...
foreach ($rule in $arr_firewall_rules) {
if ($bln_added_to_rule -ne 1) {
# Split the existing IPs from the current rule into an array so we can easily count them
$arr_existing_bad_ips_current_rule = $rule.RemoteAddresses -split(',')
# If the number of IPs to add is less than 1000 minus the current number of IPs in the rule, add them to this rule
if ($arr_new_bad_ips_for_firewall.Count -le (1000 - $arr_existing_bad_ips_current_rule.Count)) {
# Add new IPs to firewall rule
$arr_new_bad_ips_for_firewall | %{$rule.RemoteAddresses += ',' + $_}
# Write which rule the IPs were added to to log file
echo "New IP addresses above added to Windows Firewall rule:" $rule.Name | Out-File -Append -Encoding utf8 C:\blockattackers.txt
# Set boolean so any other rules are skipped when adding IPs
$bln_added_to_rule = 1
}
}
}
# If there wasn't room in any other "BlockAttackers*" firewall rule, create a new one and add the IPs to it
if ($bln_added_to_rule -ne 1) {
$str_new_rule_name = "BlockAttackers (Created " + $current_date_utc.ToString("yyyy-MM-dd HH:mm:ss") + " UTC)"
netsh advfirewall firewall add rule dir=in action=block name=$str_new_rule_name description="Rule automatically created by BlockAttackers Powershell script written by Kevin Micke." enable=yes remoteip="0.0.0.0" | Out-Null
$new_rule = $firewall.rules | Where {$_.Name -eq $str_new_rule_name}
# Add new IPs to firewall rule
$arr_new_bad_ips_for_firewall | %{$new_rule.RemoteAddresses += ',' + $_}
# Write which rule the IPs were added to to log file
echo "New IP addresses above added to newly created Windows Firewall rule:" $new_rule.Name | Out-File -Append -Encoding utf8 C:\blockattackers.txt
}
}