O script de remuda , editado por kevinmicke (7 de fevereiro às 21:59) não checou o canal de controle do FTP, que tem uma pasta própria no meu sistema (Windows Server 2008 R2). Também 530 11001 -events não foram reconhecidos, o que parece aparecer quando o hacker apenas tenta acessar o canal de controle. Então eu adicionei algumas linhas no script para verificar uma segunda pasta de log FTP:
# 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
# Checking of the FTP's control channel added by serverfault.com user Uwe Martens
#
# 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 = 3
# 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 Control Channel log file
$str_log_file_name_control_channel = "C:\inetpub\logs\LogFiles\FTPSVC\u_ex" + $current_date_utc.ToString("yyMMdd") + ".log"
# Search today's FTP Control Channel log file for "530 1" 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_control_channel = @()
$arr_new_bad_ips_ftp_control_channel = Select-String $str_log_file_name_control_channel -pattern "530 1" |
ForEach-Object {$_.Line.Substring(20,15) -replace " .*", ""} |
Group |
Where {$_.Count -ge $int_block_limit} |
Select -property Name
# Set path to today's FTP log file
$str_log_file_name = "C:\inetpub\logs\LogFiles\FTPSVC*\u_ex" + $current_date_utc.ToString("yyMMdd") + ".log"
# Search today's FTP log file for "530 1" 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
# In FTPSVC* has to be added the ID of the FTP-server instead of *, or just take the right log-folder
$arr_new_bad_ips_ftp = @()
$arr_new_bad_ips_ftp = Select-String $str_log_file_name -pattern "530 1" |
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_control_channel) + @($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." 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 "/.*", ""}
# Enter your server's IP (IPv4 and IPv6) in line 115 and 116.
# 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('0.0.')) -and
!($_.StartsWith('10.0.')) -and
!($_.StartsWith('*.*.*.*')) -and
!($_.StartsWith('*:*:*:*:*:*'))
}
# 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:\inetpub\logs\LogFiles\blockattackers.txt
# Write newly-blocked IP addresses to log file
$arr_new_bad_ips_for_firewall | Out-File -Append -Encoding utf8 C:\inetpub\logs\LogFiles\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:\inetpub\logs\LogFiles\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." 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:\inetpub\logs\LogFiles\blockattackers.txt
}
}
O nome da pasta de log do FTP FTPSVC* na linha 54 deve ser completado por causa. Na linha 115 e 116 tem que ser inserido o IP do seu servidor (IPv4 e IPv6), caso contrário o IP do próprio servidor pode ser adicionado à regra de firewall cem vezes. A variável $int_block_limit estou configurando para 1 no meu servidor, então o script está bloqueando um ataque de hackers causando um evento do 4625 em dois segundos. Eu ainda estou pensando em executar o script adicionalmente ocorrendo 4625 eventos em um período de tempo de alguns minutos. Da causa, também seria possível separar os scripts e permitir que um script verificasse os eventos do 4625 acionados pelo evento 4625 e outro, as pastas de log do FTP, verificando periodicamente a cada 5 ou 10 minutos, mesmo com uma regra de firewall separada. e arquivo de log.