ใช้remunda 's สคริปต์ที่ดีเป็นจุดเริ่มต้นที่ฉันเพิ่มสิ่งหนึ่งที่สำคัญที่ขาดหายไป: การปิดกั้นที่อยู่ IP ล้มเหลวในการเข้าสู่ระบบ Windows Server จะไม่บันทึกที่อยู่ IP ไปที่บันทึกการรักษาความปลอดภัยเมื่อมีคนไม่สามารถเข้าสู่ระบบผ่าน FTP ได้ แต่จะตั้งค่า "ที่อยู่เครือข่ายต้นทาง" เป็นเส้นประแทน FTP เป็นเวกเตอร์การโจมตีที่พบบ่อยมากสำหรับการโจมตีแบบ brute force ดังนั้นฉันจึงเพิ่มความสามารถในการสแกนบันทึก FTP ของวันปัจจุบันสำหรับความล้มเหลวในการเข้าสู่ระบบหลายครั้งและบล็อกที่อยู่ IP เหล่านั้นด้วย
อัปเดต 2014/02/07:เมื่อฉันทำ tweaks นี้เพื่อประมวลผลบันทึก FTP เก่าทั้งหมดของฉันฉันรู้ว่าเมื่อพวกเขามีความพยายามมากมาย (50,000+), อาร์เรย์ที่สร้างขึ้นจะมีขนาดใหญ่และทำให้การประมวลผลช้าอย่างไม่น่าเชื่อ ฉันได้เขียนมันใหม่เพื่อให้มีประสิทธิภาพมากขึ้นเมื่อประมวลผลบันทึก FTP
ฉันยังพบว่ามีขีด จำกัด ที่ จำกัด โดยพลการของ 1,000 สำหรับจำนวน IP ที่สามารถอยู่ในกฎ Windows Firewall หนึ่งกฎ เนื่องจากข้อ จำกัด นั้นฉันต้องการให้สร้างกฎใหม่โดยอัตโนมัติเมื่อกฎล่าสุดเต็ม ตอนนี้ก็ทำเช่นนั้นและยังสร้างกฎไฟร์วอลล์เริ่มต้น (ถ้าคุณไม่ได้สร้างของคุณเอง) เพื่อให้การตั้งค่าเดียวที่ต้องทำคือการเพิ่มลงใน Scheduler เพื่อทำงานเมื่อมีเหตุการณ์ 4625
นี่คือรหัสที่ได้รับการทดสอบบน Windows Server 2008 R2 และ 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
}
}