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

47

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

Nous sommes très durement frappés par des attaques par force brute qui tentent de deviner les noms d'utilisateurs. Cela aiderait donc beaucoup à alléger le serveur.

HeavyWave
la source
8
* nix a fial2ban ... ne sait pas s’il existe un équivalent / port Windows. fail2ban.org/wiki/index.php/Main_Page
Chris Nava
5
De Evan Anderson: serverfault.com/questions/43360/… ... semble être un bon équivalent des fonctionnalités de fail2ban, mais comme votre question n'est pas suffisamment précise, je ne sais pas si vous souhaitez interdire les adresses IP qui tentent de se connecter. sur un site Web hébergé, votre serveur (via SSH) ou votre domaine. Une clarification irait un long chemin. De plus, vous pouvez évaluer la limite sur votre pare-feu, mais cela dépend de la mise en œuvre.
4
Vous voudrez peut-être consulter serverfault.com/questions/216995/… pour une discussion précédente sur l'utilité de l'interdiction automatisée basée sur IP.
pehrs
1
Si vous parlez des services Terminal Server / Bureau à distance, regardez ici: serverfault.com/a/335976/7200
Evan Anderson
3
J'ai créé un service Windows sur github pour faire exactement cela: github.com/jjxtra/Windows-IP-Ban-Service
jjxtra

Réponses:

28

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 un événement spécifié par EventLog ("audit failure"). Si de nombreuses adresses IP ont échoué, il est ajouté à 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). Cependant, vous pouvez configurer le déclencheur pour qu'il s'exécute, par exemple, deux fois par heure afin d'éviter un chargement inutile du serveur.

Déclencheur de planificateur

et après le déclencheur, 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é.

exécution du script PowerShell

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

remunda
la source
1
Excellent script @remunda - merci! Je recevais aussi beaucoup de fichiers 4625 de FTP, pour lesquels le journal de sécurité n'a pas d'adresse IP, alors j'ai développé votre script pour qu'il vérifie également le journal FTP du jour. S'il vous plaît voir ma réponse ci-dessous pour plus d'informations: serverfault.com/a/571903/107701
kevinmicke
Il existe de nombreux pièges et incidents avec les journaux des événements, la consignation des adresses IP, etc., que j'ai gérés dans IPBan - source libre et open source à l' adresse github.com/jjxtra/Windows-IP-Ban-Service
jjxtra
7

Je sais que cette question est ancienne, mais c’est en fait le premier message de forum que j’ai trébuché lorsque j’ai commencé à essayer de faire exactement la même chose il ya deux semaines. J'ai réussi à créer un script qui analyse les journaux d'événements 24 heures en arrière pour les entrées de journal d'événements de connexion incorrectes, récupère celles ayant plus de 10 connexions incorrectes, puis les place dans une liste de filtres ipsec à l'aide du commande netsh. Ensuite, j'ai écrit un fichier de commandes avec cette ligne powershell .\*scriptname.ps1*et créé une tâche planifiée pour exécuter le fichier de commandes toutes les 24 heures (pour une raison quelconque, il ne serait pas exécuté 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 lorsque j'ai commencé à travailler là-dessus, je n'avais absolument aucune expérience de PowerShell. Mon habileté à optimiser les scripts laisse donc beaucoup à désirer. Cependant, malgré ce fait, j'ai pensé partager cela avec tous ceux qui pourraient l'utiliser.

Je remercie Remunda de m'avoir donné l’idée initiale, c’est cette affiche qui m’a fait comprendre l’idée d’utiliser PowerShell pour effectuer une recherche dans les journaux des événements.

Keegan
la source
4

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

Un bon conseil est de créer la règle "BlockAttackers" qui bloque les adresses IP MAIS le rendre désactivé au début. Ensuite, exécutez ce script une fois manuellement pour qu’il puisse renseigner le champ "RemoteAddresses" avec les adresses IP à bloquer. Examinez ces adresses IP pour vous assurer qu'aucun élément essentiel n'a été ajouté, puis activez la règle de pare-feu. Ajoutez cette règle à votre pare-feu comme décrit précédemment.

Le git pour 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'}
}
Michael Khalili
la source
2

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.

Thorsten
la source
1
+1, c'est un excellent moyen de vous préparer à une attaque par déni de service. Et si vous utilisez une limitation du débit, la plupart des outils de force brute automatisés espacent suffisamment leurs tentatives de connexion pour ne pas se faire prendre.
12
L'interdiction automatique des adresses IP après un certain nombre d'échecs de connexion est une pratique très courante. Je vois des hôtes bannis toutes les heures après avoir essayé de deviner les mots de passe FTP. Cela ne peut être une attaque par déni de service que si quelqu'un parvient à usurper votre adresse IP (impossible avec les connexions TCP), ou si vous vous trompez plusieurs fois de mot de passe (dans ce cas, ce n'est pas quelqu'un d'autre qui contrôle les règles du pare-feu, c'est vous)
devicenull
18
Désolé, mais je n'ai pas demandé si c'était une bonne idée.
HeavyWave
1
Bien entendu, il n’existe aucune raison pour que des exceptions ne puissent pas être définies pour une ou plusieurs adresses IP spécifiques, ce qui éliminerait pratiquement le problème de déni de service.
John Gardeniers
2

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 courant, j'ai supprimé cette partie car elle causait 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 Remote Desktop avec une authentification au niveau d'accès réseau sous Windows 2008, vous devrez peut-être suivre l'astuce suivante. Windows 2008 n'a pas d'adresse IP dans le journal de sécurité et ne semble pas en avoir dans le journal Microsoft-Windows-RemoteDesktopServices-RdpCoreTS. J'ai donc dû utiliser 2 journaux - associer 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 un travail approximatif, mais il semble détecter les attaques par mot de passe. Voici la partie qui recueille les IP violant:

    $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 du pare-feu. NOTE 2: Je ne suis pas un expert en PowerShell. Ce serait donc bien que certains gourous puissent corriger / améliorer mon code.

Calmez-vous
la source
1

J'utilise ts_block freeby.

En gros, il s'agit d'un "programme VBScript qui agit en tant que récepteur d'événements WMI pour recevoir les événements consigné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 laisser le journal enregistrer les tentatives, puis interdire en fonction du nombre de tentatives autorisées, et / ou vous pouvez coder en dur les noms de connexion auxquels vous ne voulez pas donner accès.

Je me suis fait prendre en ajoutant accidentellement le même nom deux fois et le service passe simplement dans une boucle sans fin qui redémarre toutes 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. Des noms de connexion du type «admin», «Admin», «Administrateur», «invité», etc. sont automatiquement bannis. Devrait être simple à changer pour ip?

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

chipbug
la source
0

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

joeqwerty
la source
0

Si un serveur Web est attaqué, vous pouvez installer l' extension de restrictions IP dynamiques . S'il s'agit d'une authentification standard sur le serveur, vous devriez pouvoir implémenter une isolation de domaine et de serveur qui limiterait la portée des attaques aux ordinateurs appartenant à un domaine et pourrait être configurée pour autoriser uniquement les tentatives des systèmes auxquels vous avez besoin d'accéder. le serveur. Dans Windows, la prévention des attaques par force brute consiste à définir la stratégie de verrouillage du compte sur 10 minutes et une stratégie de mot de passe incorrect sur 3 tentatives. Cela signifie que le compte attaqué serait verrouillé 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 cela prend par seconde pour avoir un impact sur le système)

Jim B
la source
Sur mon petit exemple AWS, une tentative toutes les 4 secondes est suffisante pour consommer 50% du processeur. Jolie merde si vous me demandez ...
RomanSt
Wow, je me demande pourquoi l'utilisation est si élevée. Il faudra que je fasse quelques tests pour voir combien de tentatives d'enregistrement de mes ordinateurs virtuels sont nécessaires.
Jim B
Je pense que c'est parce que je n'utilise pas l'authentification au niveau du réseau. Par conséquent, chaque tentative de connexion génère une interface utilisateur de connexion à présenter à l'attaquant via une session de bureau à distance. Je peux voir pourquoi cela pourrait être cher.
RomanSt
0

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 (Install & done), vous pouvez trouver un outil gratuit ici, et devriez probablement continuer à lire ceci:

Version actuelle: 1.2 (profil client .NET Framework 4.0) -> Téléchargez la version actuelle de EvlWatcher (gratuite pour un usage personnel et commercial)

Nouveau dans la version 1.2 (plus d’informations dans la documentation):

  • Console de gestion
  • Modèle de service WCF
  • Liste noire
  • Déplacement automatique vers la liste noire après 3 tentatives (par défaut)

Pour les serveurs plus anciens (.NET Framework 2.0)

-> Téléchargez la version réduite de EvlWatcher (gratuit pour usage personnel et commercial)

Mastro
la source
0

En utilisant le script génial de Remunda comme point de départ, j’ai ajouté une chose qui manquait: le blocage des adresses IP à partir des connexions FTP ayant échoué . Windows Server n'enregistre pas l'adresse IP dans le journal de sécurité lorsqu'un utilisateur ne parvient pas à se connecter via FTP. Il définit plutôt "Adresse réseau source" sur un tiret. Le FTP étant un vecteur d’attaque très courant pour les attaques par force brute, j’ai ajouté à son script la possibilité d’analyser les journaux FTP du jour pour rechercher les échecs de connexion multiples et de bloquer également les adresses IP.

Mise à jour 2014/02/07: Lorsque j'ai apporté quelques modifications à cette question pour traiter tous mes anciens journaux FTP, je me suis rendu compte que, lorsqu'elles avaient un nombre de tentatives immense (plus de 50 000), les baies créées étaient énormes et rendaient le traitement extrêmement lent. Depuis, je l'ai réécrit pour le rendre beaucoup plus efficace lors du traitement des journaux FTP.

J'ai également découvert qu'il existe une limite absolue arbitraire de 1 000 pour le nombre d'adresses IP pouvant figurer dans une règle de pare-feu Windows. À cause de cette limite, j'avais besoin de créer automatiquement une nouvelle règle lorsque la dernière est remplie. Il le fait maintenant 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 consiste à l'ajouter au planificateur afin qu'il soit exécuté lorsqu'il y a un é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
    }
}
kevinmicke
la source
FYI: Pour ceux qui n'ont jamais exécuté de script PowerShell sur un système, vous devez d'abord ouvrir un nouveau PowerShell et le lancer Set-ExecutionPolicy RemoteSignedpour pouvoir exécuter des scripts locaux. Sinon, vous obtiendrez une erreur: "blockattackers.ps1 ne peut pas être chargé car l'exécution des scripts est désactivée sur ce système."
kevinmicke
0

Le script de remuda , édité par kevinmicke (7 février à 21h59) n'a pas vérifié le canal de contrôle du FTP, qui possède un propre dossier sur mon système (Windows Server 2008 R2). De plus, 530 11001-events n'a pas été reconnu, ce qui semble apparaître lorsque le pirate tente uniquement d'accéder au canal de contrôle. J'ai donc ajouté quelques lignes dans le script pour vérifier un deuxième dossier FTP-log-folder:

# Ce script Windows Powershell bloque automatiquement les adresses IP qui tentent de se connecter au système.
# et échouez le nombre de fois défini ci-dessous avec la variable $ int_block_limit ou plus. Est scanne à la fois la sécurité
# log, qui couvre Remote Desktop et d'autres tentatives, ainsi que le journal FTP du jour actuel. Si le $ int_block_limit
# limit est atteint sur l’un de ces journaux (séparément, pas combiné), l’adresse IP sera ajoutée à la
# règle de pare-feu.
#
# Le script créera automatiquement une règle de pare-feu nommée "BlockAttackers (Créé aaaa-MM-jj HH: mm: ss UTC)" à l'aide de
# l'heure actuelle si le nom "BlockAttackers" n'existe pas déjà. Parce qu'il y a un dur
# limite de 1000 entrées (adresses IP) que vous pouvez bloquer par règle, il créera également des règles de même nom une fois que
La limite est atteinte pour la dernière.
#
# Je recommande de configurer le script pour qu’il s’exécute comme une tâche planifiée déclenchée par l’échec de l’audit de connexion à l’événement 4625 à partir de la
# Le journal de sécurité, ou bien vous pouvez le configurer pour qu'il s'exécute après un certain temps (c'est-à-dire toutes les 10 minutes).
#
# Auteurs:
# La majorité du script écrit par l'utilisateur de serverfault.com kevinmicke
# Journal de sécurité Windows écrit par serverfault.com user remunda, qui a servi de point de départ à kevinmicke
# Vérification du canal de contrôle du FTP ajouté par l'utilisateur de serverfault.com Uwe Martens
#
# Détails: https://serverfault.com/questions/233222/ban-ip-address-based-on-x-number-of-unsuccessful-login-attempts


# Définir le nombre de tentatives de connexion infructueuses après lesquelles une adresse IP sera bloquée
$ int_block_limit = 3

# Fenêtre de temps pendant laquelle consulter le journal de sécurité, actuellement configuré pour vérifier uniquement les dernières 24 heures
$ dat_time_window = [DateTime] :: Now.AddDays (-1)

# Sélectionnez dans le journal de sécurité toutes les adresses IP ayant plus de $ échecs d'audit int_block_limit (événement 4625) dans $ 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]}} |
    Propriété-groupe-propriété IpAddress |
    Où {$ _. Count -ge $ int_block_limit} |
    Sélectionnez le nom de la propriété

# Obtenir l'heure UTC actuelle pour trouver le nom de fichier du journal FTP actuel
$ current_date_utc = (Get-Date) .ToUniversalTime ()

# Définir le chemin d'accès au fichier journal du canal de contrôle FTP actuel
$ str_log_file_name_control_channel = "C: \ inetpub \ logs \ LogFiles \ FTPSVC \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Recherchez dans le fichier journal du canal de contrôle FTP actuel "530 1" pour trouver les lignes contenant les adresses IP des systèmes dont la connexion a échoué.
# récupère uniquement l’adresse IP de chaque ligne, groupez les adresses IP par adresse IP pour compter les tentatives de chaque
# IP qui ont $ int_block_limit ou plus de mauvaises connexions aujourd'hui
$ arr_new_bad_ips_ftp_control_channel = @ ()
$ arr_new_bad_ips_ftp_control_channel = Chaîne de sélection $ str_log_nom_fichier_control_channel -pattern "530 1" |
    ForEach-Object {$ _. Line.Substring (20,15) -remplace ". *", ""} |
    Groupe |
    Où {$ _. Count -ge $ int_block_limit} |
    Sélectionnez le nom de la propriété

# Définir le chemin d'accès au fichier journal FTP actuel
$ str_log_file_name = "C: \ inetpub \ logs \ LogFiles \ FTPSVC * \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Recherchez dans le fichier journal FTP d'aujourd'hui "530 1" pour trouver les lignes contenant les adresses IP des systèmes avec lesquels la connexion a échoué.
# récupère uniquement l’adresse IP de chaque ligne, groupez les adresses IP par adresse IP pour compter les tentatives de chaque
# IP qui ont $ int_block_limit ou plus de mauvaises connexions aujourd'hui
# Dans FTPSVC *, il faut ajouter l'ID du serveur FTP au lieu de * ou prendre simplement le bon dossier de journal
$ arr_new_bad_ips_ftp = @ ()
$ arr_new_bad_ips_ftp = Chaîne de sélection $ str_log_nom_fichier -pattern "530 1" |
    ForEach-Object {$ _. Line.Substring (20,15) -remplace ". *", ""} |
    Groupe |
    Où {$ _. Count -ge $ int_block_limit} |
    Sélectionnez le nom de la propriété

# Concaténer les deux tableaux d'adresses IP (un du journal de sécurité, un du journal FTP)
$ 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)

# Triez le tableau en sélectionnant uniquement les adresses IP uniques (au cas où une adresse IP apparaît dans les journaux de sécurité et FTP)
$ arr_new_bad_ips_all_sorted = @ ()
$ arr_new_bad_ips_all_sorted = $ arr_new_bad_ips_all |
    Foreach-Object {[string] $ _. Name} |
    Select-Object -unique

# Obtenir un objet pare-feu
$ firewall = New-Object -comobject hnetcfg.fwpolicy2

# Obtenir toutes les règles de pare-feu correspondant à "BlockAttackers *"
$ arr_firewall_rules = $ firewall.Rules | Où {$ _. Name-like 'BlockAttackers *'}

# Si aucune règle de pare-feu "BlockAttackers *" n'existe encore, créez-en une et définissez-la comme variable
if ($ arr_firewall_rules -eq $ null) {
    $ str_new_rule_name = "BlockAttackers (Créé" + $ current_date_utc.ToString ("aaaa-MM-jj HH: mm: ss") + "UTC)"
    Pare-feu netsh advfirewall add rule dir = in action = nom du bloc = $ str_new_rule_name description = "La règle a été créée automatiquement." enable = yes remoteip = "0.0.0.0" | Out-Null
    $ arr_firewall_rules = $ firewall.Rules | Où {$ _. Name-like 'BlockAttackers *'}
}

# Divisez les IP existantes des règles de pare-feu "BlockAttackers *" actuelles en un tableau afin que nous puissions les rechercher facilement.
$ arr_existing_bad_ips = @ ()
foreach ($ rule dans $ arr_firewall_rules) {
    $ arr_existing_bad_ips + = $ rule.RemoteAddresses -split (',')
}

# Supprimer les masques de sous-réseau des adresses IP actuellement bloquées par la ou les règles de pare-feu
$ arr_existing_bad_ips_without_masks = @ ()
$ arr_existing_bad_ips_without_masks = $ arr_existing_bad_ips | ForEach-Object {$ _ -replace "/.*", ""}

# Entrez l’IP de votre serveur (IPv4 et IPv6) aux lignes 115 et 116.
# Sélectionnez les adresses IP à ajouter au pare-feu, mais uniquement celles qui ...
$ arr_new_bad_ips_for_firewall = @ ()
$ arr_new_bad_ips_for_firewall = $ arr_new_bad_ips_all_sorted | Où {
    # contient une adresse IP (c’est-à-dire qu’ils ne sont ni vierges ni un tiret, ce que le journal de sécurité contient pour les systèmes ayant échoué la connexion FTP)
    $ _. Longueur -gt 6 -et
    # ne sont pas déjà dans les règles de pare-feu
    ! ($ arr_existing_bad_ips_without_masks -contains $ _) -et
    # ne sont pas le bouclage local
    ! ($ _. StartsWith ('127.0.0.1')) - et
    # ne font pas partie du sous-réseau local
    ! ($ _. StartsWith ('192.168.')) - et
    ! ($ _. StartsWith ('0.0.')) Et
    ! ($ _. StartsWith ('10 .0. ')) - et
    ! ($ _. StartsWith ('*. *. *. *')) -Et
    ! ($ _. StartsWith ('*: *: *: *: *: *'))
}

# S'il y a des IP à bloquer, procédez comme suit ...
if ($ arr_new_bad_ips_for_firewall -ne $ null) {
    # Ecrire la date et l'heure dans un fichier journal spécifique au script
    [DateTime] :: Maintenant | Out-File -Append -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    # Écrire les adresses IP récemment bloquées dans le fichier journal
    $ arr_new_bad_ips_for_firewall | Out-File -Append -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

    # Boolean pour s'assurer que les nouvelles adresses IP ne sont ajoutées qu'à une seule règle
    $ bln_added_to_rule = 0

    # Un tableau contenant les mauvaises adresses IP de chaque règle, une à la fois, afin que nous puissions compter pour nous assurer que l'ajout des nouvelles ne dépassera pas 1000 adresses IP
    $ arr_existing_bad_ips_current_rule = @ ()

    # Pour chaque règle "BlockAttackers *" dans le pare-feu, procédez comme suit ...
    foreach ($ rule dans $ arr_firewall_rules) {
        if ($ bln_added_to_rule -ne 1) {
            # Divisez les IP existantes de la règle actuelle dans un tableau afin que nous puissions les compter facilement
            $ arr_existing_bad_ips_current_rule = $ rule.RemoteAddresses -split (',')

            # Si le nombre d'adresses IP à ajouter est inférieur à 1 000 moins le nombre actuel d'adresses IP dans la règle, ajoutez-les à cette règle.
            if ($ arr_new_bad_ips_for_firewall.Count -le (1000 - $ arr_existing_bad_ips_current_rule.Count)) {
                # Ajouter de nouvelles adresses IP à la règle de pare-feu
                $ arr_new_bad_ips_for_firewall | % {$ rule.RemoteAddresses + = ',' + $ _}

                # Indiquez la règle à laquelle les adresses IP ont été ajoutées dans le fichier journal.
                echo "Les nouvelles adresses IP ci-dessus ajoutées à la règle de pare-feu Windows:" $ rule.Name | Out-File -Append -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

                # Définit un booléen pour que toutes les autres règles soient ignorées lors de l'ajout d'adresses IP
                $ bln_added_to_rule = 1
            }
        }
    }

    # S'il n'y avait pas de place dans une autre règle de pare-feu "BlockAttackers *", créez-en une nouvelle et ajoutez-y les adresses IP.
    if ($ bln_added_to_rule -ne 1) {
        $ str_new_rule_name = "BlockAttackers (Créé" + $ current_date_utc.ToString ("aaaa-MM-jj HH: mm: ss") + "UTC)"
        Pare-feu netsh advfirewall add rule dir = in action = nom du bloc = $ str_new_rule_name description = "La règle a été créée automatiquement." enable = yes remoteip = "0.0.0.0" | Out-Null
        $ new_rule = $ firewall.rules | Où {$ _. Name -eq $ str_new_rule_name}

        # Ajouter de nouvelles adresses IP à la règle de pare-feu
        $ arr_new_bad_ips_for_firewall | % {$ new_rule.RemoteAddresses + = ',' + $ _}

        # Indiquez la règle à laquelle les adresses IP ont été ajoutées dans le fichier journal.
        echo "Les nouvelles adresses IP ci-dessus ont été ajoutées à la règle de pare-feu Windows récemment créée:" $ new_rule.Name | Out-File -Append -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    }
}

Le nom du dossier du journal FTP FTPSVC*sur la ligne 54 doit être complété. Dans les lignes 115 et 116, vous devez entrer l'adresse IP de votre serveur (IPv4 et IPv6), sinon l'adresse IP de vos propres serveurs pourrait être ajoutée cent fois à la règle de pare-feu. La variable que $int_block_limitje mets à 1 sur mon serveur, le script bloque donc une attaque de pirates informatiques provoquant un événement 4625 en moins de deux secondes. Je pense toujours à exécuter le script en plus des événements 4625 survenus dans un laps de temps de quelques minutes. Bien entendu, il serait également possible de séparer les scripts et de laisser un script vérifier les 4625 événements déclenchés par l'événement 4625 et un autre les dossiers de journalisation du FTP vérifiant périodiquement toutes les 5 ou 10 minutes, même avec une règle de pare-feu distincte. et fichier journal.

Uwe Martens
la source
-1

J'ai ajouté le mien pour SQL

# Select from the Application log (SQL) all IP addresss that have more than $int_block_limit logon failure within $dat_time_window
$arr_new_bad_ips_SQL_log = @()
$arr_new_bad_ips_SQL_log = Get-EventLog -LogName 'Application' -After $dat_time_window |
    Where-Object{$_.EventID -eq 18456} |
    Select-Object @{n='CLIENT';e={$_.ReplacementStrings[-1]}} |
    Group-Object -property CLIENT |
    Where {$_.Count -ge $int_block_limit} |
    Select -property Name |
    {
        $_.Name = $_.Name.Replace(" [CLIENT: ", "");
        $_.Name = $_.Name.Replace("]", "");
        return $_;
    }

Ensuite, vous devrez ajouter le tableau dans ips_all

$arr_new_bad_ips_all = @($arr_new_bad_ips_SQL_log) + @($arr_new_bad_ips_security_log) + @($arr_new_bad_ips_ftp_control_channel) + @($arr_new_bad_ips_ftp)
Vince Vinci
la source