web-dev-qa-db-fra.com

Interdire l'adresse IP en fonction du nombre X de tentatives de connexion infructueuses?

Est-il possible d'interdire une adresse IP après un nombre X de tentatives de connexion infructueuses sur un serveur Windows? Pas à un compte particulier, que je sais faire, mais à l'ensemble de la machine.

Nous sommes assez durement touchés par les attaques par force brute essayant de deviner les noms d'utilisateur, donc cela aiderait vraiment à décharger le serveur.

47
HeavyWave

Vous pouvez le faire avec PowerShell et le gestionnaire de tâches. Ce n'est probablement pas une solution parfaite, mais cela fonctionne assez bien et j'ai environ 100 adresses IP bloquées en deux mois. J'ai écrit un script qui sélectionne les événements spécifiés EventLog ("échec de l'audit"). S'il y a plusieurs échecs de connexion à partir d'une adresse IP, elle est ajoutée à la règle de pare-feu (créée manuellement) nommée "BlockAttackers" qui bloque tout trafic vers les adresses IP spécifiées.

Script PS1:

$DT = [DateTime]::Now.AddDays(-1) # check only last 24 hours

$l = Get-EventLog -LogName 'Security' -InstanceId 4625 -After $DT | Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]} } # select Ip addresses that has audit failure 
$g = $l | group-object -property IpAddress  | where {$_.Count -gt 20} | Select -property Name # get ip adresses, that have more than 20 wrong logins

$fw = New-Object -ComObject hnetcfg.fwpolicy2 # get firewall object

$ar = $fw.rules | where {$_.name -eq 'BlockAttackers'} # get firewall rule named 'BlockAttackers' (must be created manually)

$arRemote = $ar.RemoteAddresses -split(',') #split the existing IPs into an array so we can easily search for existing IPs

$w = $g | where {$_.Name.Length -gt 1 -and  !($arRemote -contains $_.Name + '/255.255.255.255') } # get ip addresses that are not already in firewal rule. Include the subnet mask which is automatically added to the firewall remote IP declaration.

$w| %{$ar.remoteaddresses += ',' + $_.Name} # add IPs to firewall rule

Créez une tâche dans le planificateur et définissez le déclencheur sur l'événement 4625 (connexion Windows, y compris les services de terminal). Mais vous pouvez définir le déclencheur pour qu'il s'exécute, par ex. deux fois par heure pour éviter de charger inutilement le serveur.

Scheduler trigger

et après le déclenchement, exécutez le script powershell. Vous devez également définir des privilèges plus élevés pour exécuter ce script, sinon il échouera avec une exception de sécurité.

runing powershell script

Vous pouvez également lier ce script à d'autres événements de sécurité.

28
remunda

Je sais que cette question est ancienne, mais c'était en fait le premier message du forum que je suis tombé sur quand j'ai commencé à essayer de faire exactement la même chose il y a quelques semaines. J'ai réussi à trouver un script de travail qui analysera les journaux d'événements 24 heures en arrière pour les seules entrées de journal des événements de mauvaise connexion, récupérera celles qui ont plus de 10 mauvaises connexions, puis les mettra dans une liste de filtres IPSec à l'aide de la commande netsh. J'ai ensuite écrit un fichier batch avec cette ligne powershell .\*scriptname.ps1* et a créé une tâche planifiée pour exécuter le fichier de commandes toutes les 24 heures (pour une raison quelconque, il ne s'exécuterait pas directement).

$DATE = [DateTime]::Now.AddDays(-1)

$EVS = Get-EventLog Security -InstanceId 529 -after $DATE

$EVS | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*"  -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group-object -property $_ | where-object {$_.count -gt 10} | select-object -property name | format-list | out-file c:\rdpblock.txt 

get-content -path c:\rdpblock.txt | foreach-object {$_.replace("Name :", "")} | out-file c:\rdpblockcleaned.txt 

get-content -path c:\rdpblockcleaned.txt | select-object -unique | out-file c:\rdpblocknospaces.txt

$RDPIP = get-content -path c:\rdpblocknospaces.txt | select-object -skip 1

$RDPIP | foreach-object {$_.replace("     ", "")} | foreach-object {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$($_) dstaddr=any}

Je sais que ce script est probablement inefficace mais quand j'ai commencé à travailler dessus, je n'avais absolument aucune expérience en PowerShell, donc ma capacité à optimiser les scripts laisse beaucoup à désirer. Cependant, malgré ce fait, j'ai pensé que je partagerais cela avec tous ceux qui pourraient l'utiliser.

Je remercie Remunda de m'avoir donné l'idée initiale, cette affiche est celle qui m'a poussé à l'idée d'utiliser PowerShell pour rechercher les journaux d'événements.

7
Keegan

Ce script s'appuie sur la réponse de remunda et va un peu plus loin https://serverfault.com/a/397637/155102 Il représente la règle "BlockAttackers", aucune IP n'a encore été saisie (ce qui renvoie un " * "sous forme de chaîne). Il écrit également un commentaire dans un fichier journal pour vous indiquer quand l'IP a été ajoutée à la règle.

Une bonne astuce consiste à créer la règle "BlockAttackers" qui bloque les adresses IP MAIS la désactiver au début. Ensuite, exécutez ce script une fois manuellement afin qu'il puisse remplir le champ "RemoteAddresses" avec les adresses IP réelles qui doivent être bloquées. Jetez un œil à ces adresses IP pour vous assurer que rien de critique n'a été ajouté, puis activez la règle de pare-feu. Ajoutez cette règle à votre pare-feu comme le décrit la remunda.

Le git de ce script

#Checks for IP addresses that used incorrect password more than 10 times
#within 24 hours and blocks them using a firewall rule 'BlockAttackers'

#Check only last 24 hours
$DT = [DateTime]::Now.AddHours(-24)

#Select Ip addresses that has audit failure
$l = Get-EventLog -LogName 'Security' -InstanceId 4625 -After $DT | Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]} }

#Get ip adresses, that have more than 10 wrong logins
$g = $l | group-object -property IpAddress | where {$_.Count -gt 10} | Select -property Name

#Get firewall object
$fw = New-Object -ComObject hnetcfg.fwpolicy2

#Get firewall rule named 'BlockAttackers' (must be created manually)
$ar = $fw.rules | where {$_.name -eq 'BlockAttackers'}

#Split the existing IPs into an array so we can search it for existing IPs
$arRemote = $ar.RemoteAddresses -split(',')

#Only collect IPs that aren't already in the firewall rule
$w = $g | where {$_.Name.Length -gt 1 -and !($arRemote -contains $_.Name + '/255.255.255.255') }

#Add the new IPs to firewall rule
$w| %{
  if ($ar.RemoteAddresses -eq '*') {
    $ar.remoteaddresses = $_.Name
  }else{
    $ar.remoteaddresses += ',' + $_.Name
  }
}

#Write to logfile
if ($w.length -gt 1) {
  $w| %{(Get-Date).ToString() + ' ' + $_.Name >> '.\blocked.txt'}
}
4
Michael Khalili

Je ne peux pas prendre le crédit pour cette réponse, mais https://serverfault.com/users/7200/evan-anderson a mentionné son projet http://opensource.wellbury.com/projects/windows_sshd_block/latest-release /

3
becomingwisest

Ce n'est généralement pas une bonne idée de laisser quelqu'un d'autre contrôler vos règles de pare-feu. C'est essentiellement ce que vous demandez ici.

2
Thorsten

C'est un vieux sujet. J'utilisais le script fourni par kevinmicke en 2014-2015. Ensuite, il a juste cessé de fonctionner. J'ai donc dû le modifier un peu pour adopter l'authentification Windows Network Security qui ne laisse pas les adresses IP dans le journal de sécurité. De plus, comme je n'ai pas de FTP en cours d'exécution, j'ai supprimé cette partie car elle provoquait des erreurs car il n'y avait pas de dossier de journal. Le principal changement concerne la source des événements RDP.

    $current_date_utc = (Get-Date).ToUniversalTime()

    # 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)

    $arr_new_bad_ips_all = (get-winevent -filterhashtable @{ logname='Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational'; starttime=$dat_time_window; id=140 }).message |
        % { if ($_ -match "of (.+) failed") { $Matches[1] }} |
        Group-Object |
        Where {$_.Count -ge $int_block_limit} |
        Select -property Name

    # 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 = $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 | 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_all | 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.'))
    }

    # 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:\Security\blockattackers.txt
        # Write newly-blocked IP addresses to log file
        $arr_new_bad_ips_for_firewall | Out-File -Append -Encoding utf8 C:\Security\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:\Security\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:\Security\blockattackers.txt
        }
    }

Le script ci-dessus fonctionnera sous Windows 2012. Si vous utilisez toujours le Bureau à distance avec une authentification de niveau d'accès réseau sous Windows 2008, vous devrez peut-être effectuer l'astuce suivante. Windows 2008 n'a pas d'adresses IP dans le journal de sécurité et ne semble pas non plus les avoir dans le journal Microsoft-Windows-RemoteDesktopServices-RdpCoreTS. J'ai donc dû utiliser 2 journaux - faire correspondre les événements du journal de sécurité aux tentatives d'accès réussies au port 3389 dans le journal du pare-feu. C'est une supposition, mais il semble détecter des attaques par mot de passe. Voici la partie qui collecte les adresses IP violées:

    $current_date_utc = (Get-Date).ToUniversalTime()

    # Set number of failed login attempts after which an IP address will be blocked
    $int_block_limit = 10

    $dat_time_window = [DateTime]::Now.AddDays(-1)

    $logfn = (netsh advfirewall show allprofiles | Select-String Filename | select-object -unique | % { $_ -replace "%systemroot%",$env:systemroot }).substring(10).trimstart().trimend()

    $badevts = Get-EventLog -LogName 'Security' -InstanceId 4625 -After $dat_time_window | foreach-object { [datetime]$_.TimeWritten } | sort-object

    $fwlog = Select-String -Path $logfn -Pattern "ALLOW TCP" |
        % {
            if ($_ -match "(201.-..-..) (.+) ALLOW TCP (.+) (.+) (.+) 3389") 
            {
                new-object psobject -property @{ 
                  dt = $Matches[1] + ' ' + $Matches[2]
                  ip = $Matches[3]
                }
            }
        }

    $ipa = @()
    $j = 0

    for ($i=0; $i -lt $fwlog.Count; $i++)
    {
        $conn = ([datetime]$fwlog[$i].dt).ticks
        while (($j -lt $badevts.Count) -and (($badevts[$j]).ticks -lt $conn)) { $j++ }
        if ($j -ge $badevts.Count) { break }
        if ((($badevts[$j]).ticks - $conn) -le 30000000) { $ipa += ,($fwlog[$i].ip) }
    }

    $arr_new_bad_ips_all = $ipa |
        Group-Object |
        Where {$_.Count -ge $int_block_limit} |
        Select -property Name

REMARQUE: N'oubliez pas d'activer les journaux de pare-feu. REMARQUE 2: Je ne suis pas un expert PowerShell, il serait donc bien que certains gourous puissent corriger/améliorer mon code.

2
Calm Down

J'utilise ts_block freeby.

Fondamentalement, il s'agit d'un "programme VBScript qui agit comme un récepteur d'événements WMI pour recevoir les événements enregistrés par Windows en réponse à des ouvertures de session des services Terminal Server non valides".

Semble fonctionner parfaitement, et le script est simple si vous avez besoin de le modifier. Vous pouvez soit le laisser enregistrer les tentatives, puis l'interdire en fonction du nombre de tentatives autorisées, et/ou vous pouvez coder en dur les noms de connexion auxquels vous ne souhaitez pas donner accès.

Je me suis fait prendre par accidentellement en ajoutant deux fois le même nom et le service entre dans une boucle sans fin qui redémarre tous les 1500 ms, mais très facile à corriger/modifier si vous êtes d'accord avec vbs.

Mes paramètres actuels ne sont qu'une nouvelle tentative et vous êtes banni pendant 2 jours, avec des connexions comme "admin" "Admin" "Administrateur" "invité", etc. automatiquement interdites. Devrait-il être simple de passer à l'ip?

Un peu addictif d'aller voir quelles créatures ont été interdites du jour au lendemain ...

1
chipbug

Voulez-vous dire la connexion au serveur/domaine ou la connexion à un site Web fonctionnant sur le serveur? Si vous voulez vous connecter au serveur/domaine, la réponse est non. Windows n'a pas de concept de blocage des adresses IP en cas d'échec des tentatives de connexion, car les adresses IP ne sont pas des entités de sécurité. Il peut y avoir des outils tiers qui peuvent le faire, mais je n'en connais aucun car je ne l'ai jamais examiné.

0
joeqwerty

S'il y a un serveur Web qui est attaqué, vous pouvez installer extension des restrictions IP dynamiques . S'il s'agit d'une authentification standard auprès du serveur, vous devriez pouvoir implémenter isolation du domaine et du serveur qui limiterait la portée des attaques aux ordinateurs joints au domaine et pourrait être défini pour n'autoriser que les tentatives de systèmes dont vous avez besoin pour accéder au serveur. Dans Windows, la prévention des attaques par force brute consiste à définir la stratégie de verrouillage du compte sur un paramètre comme 10 minutes et une stratégie de mot de passe incorrect à 3 tentatives - cela signifie que le compte attaqué se verrouille pendant 10 minutes après 3 tentatives. Les connexions IP ne sont pas verrouillables par défaut dans Windows. (En passant, je suis également curieux de savoir combien de tentatives de connexion il faut par seconde pour avoir un impact sur le système)

0
Jim B

http://nerderies.blogspot.co.at/2012/12/automatically-banning-ips-with-windows.html

Si ce que vous voulez est une solution prête à l'emploi (Installer et terminé), vous pouvez trouver un outil gratuit ici, et vous devriez probablement continuer à lire ceci:

Version actuelle: 1.2 (.NET Framework 4.0 Client Profile) -> Télécharger la version actuelle d'EvlWatcher (gratuite pour un usage personnel et commercial)

Nouveau dans 1.2 (plus d'informations dans la documentation):

  • Console de gestion
  • Modèle de service WCF
  • Liste noire
  • Passage automatique à la liste noire après 3 frappes (par défaut)

Pour les serveurs plus anciens (.NET Framework 2.0)

-> Téléchargez la version réduite d'EvlWatcher (gratuite pour un usage personnel et commercial)

0
Mastro

En utilisant le grand script de remunda comme point de départ, j'ai ajouté la seule chose importante qui manquait: le blocage des adresses IP des échecs de connexion FTP . Windows Server n'enregistre pas l'adresse IP dans le journal de sécurité lorsque quelqu'un ne parvient pas à se connecter via FTP, mais définit à la place "l'adresse réseau source" sur un tiret. Le FTP est un vecteur d'attaque très courant pour les attaques par force brute, j'ai donc ajouté à son script la possibilité d'analyser les journaux FTP du jour pour détecter plusieurs échecs de connexion et bloquer également ces adresses IP.

Mise à jour 2014/02/07: Quand j'ai apporté quelques modifications à cela pour traiter tous mes anciens journaux FTP, je me suis rendu compte qu'ils avaient eu un nombre immense de tentatives ( 50000+), les tableaux qu'il a créés seraient énormes et rendraient le traitement incroyablement lent. Je l'ai depuis réécrit pour le rendre beaucoup plus efficace lors du traitement des journaux FTP.

J'ai également découvert qu'il existe une limite fixe arbitraire de 1000 pour le nombre d'adresses IP dans une règle de pare-feu Windows. En raison de cette limite, j'en avais besoin pour créer automatiquement une nouvelle règle lorsque la dernière se remplit. Il fait maintenant cela et crée également la règle de pare-feu initiale (si vous ne créez pas la vôtre) de sorte que la seule configuration à faire est de l'ajouter au planificateur pour qu'il s'exécute en cas d'événement 4625.

Voici le code, qui a été testé sur Windows Server 2008 R2 et 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
    }
}
0
kevinmicke