¿Prohibir la dirección IP en función del número X de intentos de inicio de sesión fallidos?

47

¿Es posible prohibir una dirección IP después de X número de intentos de inicio de sesión fallidos en un servidor de Windows? No a una cuenta en particular, que sé cómo hacer, sino a toda la máquina.

Los ataques de fuerza bruta nos golpean con bastante fuerza al tratar de adivinar los nombres de usuario, por lo que esto realmente ayudaría a quitar algo de carga del servidor.

HeavyWave
fuente
8
* nix tiene fial2ban ... no estoy seguro si hay un equivalente / puerto de Windows. fail2ban.org/wiki/index.php/Main_Page
Chris Nava
55
De Evan Anderson: serverfault.com/questions/43360/… ... parece ser un buen equivalente de la funcionalidad de fail2ban, pero como su pregunta no es lo suficientemente específica, no sé si está buscando prohibir las IP que intentan iniciar sesión a un sitio web alojado, su servidor (a través de SSH) o su dominio. La aclaración sería de gran ayuda. Además, podría calificar el límite en su firewall, pero eso depende de la implementación.
44
Es posible que desee echar un vistazo a serverfault.com/questions/216995/… para una discusión previa sobre la utilidad de la prohibición automatizada basada en IP.
Pehrs
1
Si está hablando de Terminal Services / Remote Desktop, eche un vistazo aquí: serverfault.com/a/335976/7200
Evan Anderson el
3
Hice un servicio de Windows en Github para hacer precisamente eso: github.com/jjxtra/Windows-IP-Ban-Service
jjxtra

Respuestas:

28

Puede hacer esto con powershell y el administrador de tareas. Probablemente no sea la solución perfecta, pero funciona bastante bien y tengo alrededor de 100 direcciones IP bloqueadas en dos meses. Escribí un script, que selecciona de eventos especificados EventLog ("error de auditoría"). Si hay muchos inicios de sesión fallidos desde cualquier dirección IP, se agrega a la regla de firewall (creada manualmente) llamada "BlockAttackers" que bloquea el tráfico a las direcciones IP especificadas.

Guión de 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

Cree una tarea en el planificador y configure el desencadenador en el evento 4625 (inicio de sesión de Windows, incluidos los servicios de terminal) Pero puede configurar el disparador para que se ejecute, por ejemplo, dos veces por hora para evitar la carga innecesaria del servidor.

Programador de disparo

y después del disparador ejecute el script de powershell También debe establecer privilegios más altos para ejecutar este script, de lo contrario fallará con la excepción de seguridad.

script de ejecución de PowerShell

También puede vincular este script a otros eventos de seguridad.

remunda
fuente
1
Excelente script @remunda - ¡gracias! También recibí muchos 4625s de FTP, para los cuales el registro de seguridad no tiene direcciones IP, por lo que amplié su script para que verifique también el registro FTP del día actual. Consulte mi respuesta a continuación para obtener más información: serverfault.com/a/571903/107701
kevinmicke
Hay muchas trampas y casos extremos con los registros de eventos, el registro de direcciones IP, etc., que he manejado en IPBan, gratuito y de código abierto en github.com/jjxtra/Windows-IP-Ban-Service
jjxtra
7

Sé que esta pregunta es antigua, pero en realidad fue la primera publicación en el foro con la que me topé cuando comencé a intentar hacer exactamente lo mismo hace un par de semanas. Me las arreglé para crear un script de trabajo que analizará los registros de eventos 24 horas atrás solo para las entradas de registro de eventos de inicio de sesión incorrectos, tome los que tengan más de 10 inicios de sesión incorrectos y luego los coloque en una lista de filtros ipsec usando el comando netsh Luego escribí un archivo por lotes con esta línea powershell .\*scriptname.ps1*y creé una tarea programada para ejecutar el archivo por lotes cada 24 horas (por alguna razón no se ejecutaría directamente).

$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}

Sé que este script es probablemente ineficiente, pero cuando comencé a trabajar en esto no tenía absolutamente ninguna experiencia en PowerShell, por lo que mi capacidad para optimizar los scripts deja mucho que desear. Sin embargo, a pesar de este hecho, pensé en compartir esto con cualquiera que pudiera usarlo.

Agradezco a Remunda por darme la idea inicial, ese póster es el que me llevó a la idea de usar PowerShell para buscar en los registros de eventos.

Keegan
fuente
4

Esta secuencia de comandos se basa en la respuesta de Remunda y va un poco más allá https://serverfault.com/a/397637/155102 Da cuenta de que la regla "BlockAttackers" aún no tiene ninguna IP ingresada (que devuelve un "*" como una cadena). También escribe un comentario en un archivo de registro para informarle cuándo se agregó la IP a la regla.

Un buen consejo es crear la regla "BlockAttackers" que bloquea las direcciones IP PERO al principio la deshabilita. Luego, ejecute este script una vez manualmente para que pueda completar el campo "RemoteAddresses" con direcciones IP reales que deben bloquearse. Eche un vistazo a esas direcciones IP para asegurarse de que no se haya agregado nada crítico y luego habilite la regla del firewall. Agregue esta regla a su firewall como se describe en remunda.

El git para este guión

#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
fuente
2

Por lo general, no es una buena idea dejar que otra persona controle las reglas de su firewall. Eso es básicamente lo que estás pidiendo aquí.

Thorsten
fuente
1
+1, esta es una excelente manera de prepararse para un ataque de denegación de servicio. Y si usa herramientas de fuerza bruta con limitación de velocidad, la mayoría de las herramientas de fuerza bruta automatizadas espacian sus intentos de inicio de sesión lo suficientemente separados para evitar ser atrapados.
12
Prohibir automáticamente las direcciones IP después de un cierto número de inicios de sesión fallidos es una práctica muy común. Veo que los hosts están prohibidos cada hora después de intentar adivinar las contraseñas FTP. La única forma en que esto puede ser un ataque DoS es si alguien logró falsificar su IP (imposible en las conexiones TCP), o si escribe incorrectamente su contraseña (en cuyo caso no es otra persona la que controla las reglas del firewall, es usted)
devicenull
18
Lo siento, pero no pregunté si era una buena idea.
HeavyWave
1
Por supuesto, no hay ninguna razón por la que no se puedan establecer excepciones para una o más direcciones IP específicas, lo que eliminaría la preocupación de DoS.
John Gardeniers
2

Este es un viejo hilo. Estaba usando el script proporcionado por kevinmicke en 2014-2015. Entonces simplemente dejó de funcionar. Así que tuve que editarlo un poco para adoptar la autenticación de Windows Network Security que no deja direcciones IP en el registro de seguridad. Además, como no tengo FTP en ejecución, eliminé esa parte porque estaba causando errores porque no había una carpeta de registro. El cambio principal está en la fuente de los eventos 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
        }
    }

La secuencia de comandos anterior funcionará en Windows 2012. Si todavía está utilizando Escritorio remoto con autenticación de nivel de acceso a la red en Windows 2008, es posible que deba hacer el siguiente truco. Windows 2008 no tiene direcciones IP en el registro de seguridad y tampoco parece tenerlas en el registro Microsoft-Windows-RemoteDesktopServices-RdpCoreTS. Así que tuve que usar 2 registros: eventos de coincidencia del registro de seguridad con intentos de acceso exitosos al puerto 3389 en el registro del firewall. Esta es una suposición, pero parece estar detectando ataques de contraseña. Aquí está la parte que recopila las IPs infractoras:

    $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

NOTA: No olvide habilitar los registros de firewall. NOTA 2: No soy un experto en PowerShell, por lo que sería bueno que algunos gurús puedan corregir / mejorar mi código.

Cálmese
fuente
1

Estoy usando ts_block freeby.

Básicamente es un "programa VBScript que actúa como un receptor de eventos WMI para recibir eventos registrados por Windows en respuesta a inicios de sesión no válidos de Terminal Services".

Parece funcionar perfectamente, y el script es sencillo si necesita modificarlo. Puede dejar que registre los intentos y luego prohibirlos según la cantidad de intentos permitidos, y / o puede codificar los nombres de inicio de sesión a los que no desea dar acceso.

Me sorprendió agregar accidentalmente el mismo nombre dos veces y el servicio simplemente entra en un bucle sin fin que se reinicia cada 1500 ms, pero es muy fácil de arreglar / modificar si está bien con vbs.

Mi configuración actual es solo un reintento y está prohibido durante 2 días, con inicios de sesión como 'admin' 'Admin' 'Administrador' 'invitado', etc., automáticamente prohibido. ¿Debería ser sencillo cambiar a ip?

Un poco adictivo para entrar y ver qué criaturas han sido prohibidas de la noche a la mañana ...

chipbug
fuente
0

¿Quiere decir iniciar sesión en el servidor / dominio o iniciar sesión en un sitio web que se ejecuta en el servidor? Si quiere iniciar sesión en el servidor / dominio, la respuesta es no. Windows no tiene el concepto de bloquear direcciones IP basadas en intentos fallidos de inicio de sesión, ya que las direcciones IP no son entidades de seguridad. Puede haber herramientas de terceros que puedan hacer esto, pero no estoy al tanto de ninguna, ya que nunca la he visto.

joeqwerty
fuente
0

Si hay un servidor web que está siendo atacado, puede instalar la extensión dinámica de restricciones de IP . Si esto es para la autenticación estándar del servidor, entonces debería poder implementar el dominio y el aislamiento del servidor, lo que limitaría el alcance de los ataques a las computadoras unidas al dominio, y podría configurarse para permitir solo los intentos de los sistemas a los que necesita tener acceso el servidor. En Windows, la prevención de ataques de fuerza bruta es establecer la política de bloqueo de la cuenta en una configuración como 10 minutos y una política de contraseña incorrecta en 3 intentos; esto significa que la cuenta que se está atacando se bloqueará durante 10 minutos después de 3 intentos. Las conexiones IP no se pueden bloquear de forma predeterminada en Windows. (Como comentario aparte, también tengo curiosidad por saber cuántos intentos de inicio de sesión tarda por segundo en afectar el sistema)

Jim B
fuente
En mi pequeña instancia de AWS, 1 intento cada 4 segundos es suficiente para consumir un 50% de CPU. Bastante basura si me preguntas ...
RomanSt
Wow, me pregunto por qué la utilización es tan alta. Tendré que hacer algunas pruebas para ver cuántos intentos se requieren para registrar mis máquinas virtuales.
Jim B
Creo que es porque no estoy usando la autenticación de nivel de red, por lo que cada intento de inicio de sesión hace girar una IU de inicio de sesión para presentarla realmente al atacante a través de una sesión de escritorio remoto. Puedo ver por qué eso puede ser costoso.
RomanSt
0

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

Si lo que desea es una solución lista para usar (Instalar y listo), puede encontrar una herramienta gratuita aquí, y probablemente debería seguir leyendo esto:

Versión actual: 1.2 (.NET Framework 4.0 Client Profile) -> Descargue la versión actual de EvlWatcher (gratis para uso personal y comercial)

Nuevo en 1.2 (más información en la documentación):

  • Consola de administración
  • Patrón de servicio WCF
  • Listas negras
  • Movimiento automático a la lista negra después de 3 golpes (por defecto)

Para servidores más antiguos (.NET Framework 2.0)

-> Descargue la versión reducida de EvlWatcher (gratis para uso personal y comercial)

Mastro
fuente
0

Utilizando el excelente script de remunda como punto de partida, agregué una cosa importante que faltaba: bloquear las direcciones IP de los inicios de sesión FTP fallidos . Windows Server no registra la dirección IP en el registro de seguridad cuando alguien no puede iniciar sesión a través de FTP, sino que establece la "Dirección de red de origen" en un guión. FTP es un vector de ataque muy común para los ataques de fuerza bruta, por lo que agregué a su script la capacidad de escanear los registros FTP del día actual en busca de múltiples fallas de inicio de sesión y también bloquear esas direcciones IP.

Actualización del 02/02/2014: cuando realicé algunos ajustes para procesar todos mis registros FTP antiguos, me di cuenta de que tenían un gran número de intentos (más de 50,000), los arreglos que creó serían enormes y el procesamiento sería increíblemente lento. Desde entonces lo reescribí para hacerlo mucho más eficiente al procesar registros FTP.

También descubrí que hay un límite estricto arbitrario de 1000 para cuántas direcciones IP pueden estar en una regla de Firewall de Windows. Debido a ese límite, lo necesitaba para crear automáticamente una nueva regla cuando se llena la última. Ahora lo hace, y también crea la regla de firewall inicial (si no crea la suya propia), de modo que la única configuración que se debe hacer es agregarla al Programador para que se ejecute cuando haya un evento 4625.

Aquí está el código, que se ha probado en Windows Server 2008 R2 y 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
fuente
FYI: Para aquellos que no han ejecutado un script de PowerShell en un sistema antes, primero deberán abrir un nuevo PowerShell y ejecutarlo Set-ExecutionPolicy RemoteSignedpara poder ejecutar los scripts locales. De lo contrario, obtendrá un error: "blockattackers.ps1 no se puede cargar porque la ejecución de scripts está deshabilitada en este sistema".
kevinmicke
0

El script de remuda , editado por kevinmicke (7 de febrero a las 21:59) no verificó el canal de control del FTP, que tiene una carpeta propia en mi sistema (Windows Server 2008 R2). Además, 530 11001no se han reconocido los eventos, que parecen aparecer cuando el pirata informático solo intenta acceder al canal de control. Así que agregué algunas líneas en el script para verificar una segunda carpeta de registro FTP:

# Este script de Windows Powershell bloqueará automáticamente las direcciones IP que intenten iniciar sesión en el sistema
# y falla el número de veces establecido a continuación con la variable $ int_block_limit o más. Es escanea tanto la seguridad
# log, que cubre el escritorio remoto y otros intentos, así como el registro FTP del día actual. Si el $ int_block_limit
# se alcanza el límite en cualquiera de esos registros (por separado, no combinado), luego la dirección IP se agregará a
# regla de firewall.
# #
# El script creará automáticamente una regla de firewall llamada "BlockAttackers (Creado aaaa-MM-dd HH: mm: ss UTC)" usando
# la hora actual si ya no existe uno con un nombre que incluya "BlockAttackers". Porque hay un duro
# límite de 1000 entradas (direcciones IP) que puede bloquear por regla, también creará reglas con nombres similares una vez que
# se alcanza el límite para el último.
# #
# Recomiendo configurar el script para que se ejecute como una tarea programada desencadenada por el evento 4625 errores de auditoría de inicio de sesión desde
# Registro de seguridad, o, alternativamente, puede configurarlo para que se ejecute después de cierto tiempo (es decir, cada 10 minutos).
# #
# Autores:
# Mayoría de guiones escritos por el usuario de serverfault.com kevinmicke
# Porción de registro de seguridad de Windows escrita por el usuario serverfault.com remunda, que proporcionó el punto de partida para kevinmicke
# Comprobación del canal de control de FTP agregado por el usuario de serverfault.com Uwe Martens
# #
# Detalles: https://serverfault.com/questions/233222/ban-ip-address-based-on-x-number-of-unsuccessful-login-attempts


# Establecer el número de intentos fallidos de inicio de sesión después del cual se bloqueará una dirección IP
$ int_block_limit = 3

# Ventana de tiempo durante la cual verificar el registro de seguridad, que actualmente está configurado para verificar solo las últimas 24 horas
$ dat_time_window = [DateTime] :: Now.AddDays (-1)

# Seleccione del registro de seguridad todas las direcciones IP que tengan más de $ int_block_limit errores de auditoría (evento 4625) dentro de $ dat_time_window
$ arr_new_bad_ips_security_log = @ ()
$ arr_new_bad_ips_security_log = Get-EventLog -LogName 'Seguridad' -InstanceId 4625 -Después $ dat_time_window |
    Select-Object @ {n = 'IpAddress'; e = {$ _. ReplacementStrings [-2]}} |
    Propiedad de objeto de grupo IpAddress |
    Donde {$ _. Count -ge $ int_block_limit} |
    Seleccione -Nombre de propiedad

# Obtenga la hora actual UTC para averiguar el nombre del archivo para el registro FTP actual
$ current_date_utc = (Get-Date) .ToUniversalTime ()

# Establecer ruta al archivo de registro del canal de control FTP de hoy
$ str_log_file_name_control_channel = "C: \ inetpub \ logs \ LogFiles \ FTPSVC \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Busque en el archivo de registro del Canal de control FTP de hoy "530 1" para encontrar líneas que contengan IP de sistemas que no pudieron iniciar sesión,
# obtenga solo la IP de cada línea, agrupe las IP por IP para contar los intentos de cada una y seleccione solo
# IP que tienen $ int_block_limit o más inicios de sesión incorrectos hoy
$ arr_new_bad_ips_ftp_control_channel = @ ()
$ arr_new_bad_ips_ftp_control_channel = Seleccionar cadena $ str_log_file_name_control_channel -pattern "530 1" |
    ForEach-Object {$ _. Line.Substring (20,15) -replace ". *", ""} |
    Grupo |
    Donde {$ _. Count -ge $ int_block_limit} |
    Seleccione -Nombre de propiedad

# Establecer ruta al archivo de registro FTP de hoy
$ str_log_file_name = "C: \ inetpub \ logs \ LogFiles \ FTPSVC * \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Busque en el archivo de registro FTP de hoy "530 1" para encontrar líneas que contengan IP de sistemas que no pudieron iniciar sesión,
# obtenga solo la IP de cada línea, agrupe las IP por IP para contar los intentos de cada una y seleccione solo
# IP que tienen $ int_block_limit o más inicios de sesión incorrectos hoy
# En FTPSVC * se debe agregar la ID del servidor FTP en lugar de *, o simplemente tomar la carpeta de registro correcta
$ arr_new_bad_ips_ftp = @ ()
$ arr_new_bad_ips_ftp = Seleccionar cadena $ str_log_file_name -pattern "530 1" |
    ForEach-Object {$ _. Line.Substring (20,15) -replace ". *", ""} |
    Grupo |
    Donde {$ _. Count -ge $ int_block_limit} |
    Seleccione -Nombre de propiedad

# Concatene las dos matrices de IP (una del registro de seguridad y otra del registro 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)

# Ordene la matriz, seleccionando solo IP únicas (en caso de que aparezca una IP en los registros de Seguridad y FTP)
$ arr_new_bad_ips_all_sorted = @ ()
$ arr_new_bad_ips_all_sorted = $ arr_new_bad_ips_all |
    Foreach-Object {[string] $ _. Name} |
    Seleccionar objeto único

# Obtener objeto de firewall
$ firewall = Objeto nuevo -objeto hnetcfg.fwpolicy2

# Obtenga todas las reglas de firewall que coincidan con "BlockAttackers *"
$ arr_firewall_rules = $ firewall.Reglas | Donde {$ _. Name -like 'BlockAttackers *'}

# Si todavía no existe una regla de firewall "BlockAttackers *", cree una y configúrela como variable
if ($ arr_firewall_rules -eq $ null) {
    $ str_new_rule_name = "BlockAttackers (Creado" + $ current_date_utc.ToString ("aaaa-MM-dd HH: mm: ss") + "UTC)"
    netsh advfirewall firewall agregar regla dir = en acción = nombre del bloque = $ str_new_rule_name description = "Regla creada automáticamente". enable = yes remoteip = "0.0.0.0" | Nulo
    $ arr_firewall_rules = $ firewall.Reglas | Donde {$ _. Name -like 'BlockAttackers *'}
}

# Divida las direcciones IP existentes de las reglas de firewall "BlockAttackers *" actuales en una matriz para que podamos buscarlas fácilmente
$ arr_existing_bad_ips = @ ()
foreach ($ regla en $ arr_firewall_rules) {
    $ arr_existing_bad_ips + = $ rule.RemoteAddresses -split (',')
}

# Limpie las máscaras de subred de las direcciones IP que actualmente están bloqueadas por las reglas del firewall
$ arr_existing_bad_ips_without_masks = @ ()
$ arr_existing_bad_ips_without_masks = $ arr_existing_bad_ips | ForEach-Object {$ _ -replace "/.*", ""}

# Ingrese la IP de su servidor (IPv4 e IPv6) en las líneas 115 y 116.
# Seleccione las direcciones IP para agregar al firewall, pero solo las que ...
$ arr_new_bad_ips_for_firewall = @ ()
$ arr_new_bad_ips_for_firewall = $ arr_new_bad_ips_all_sorted | Dónde {
    # contiene una dirección IP (es decir, no está en blanco o un guión, que el registro de seguridad tiene para los sistemas que fallaron los inicios de sesión FTP)
    $ _. Longitud -gt 6 -y
    # no están ya en la (s) regla (s) de firewall
    ! ($ arr_existing_bad_ips_without_masks -contains $ _) -y
    # no son el loopback local
    ! ($ _. Comienza con ('127.0.0.1')) -y
    # no son parte de la subred local
    ! ($ _. Comienza con ('192.168.')) -Y
    ! ($ _. Comienza con ('0.0.')) -Y
    ! ($ _. Comienza con ('10 .0. ')) -Y
    ! ($ _. Comienza con ('*. *. *. *')) -Y
    ! ($ _. ComienzaWith ('*: *: *: *: *: *'))
}

# Si hay IP para bloquear, haga lo siguiente ...
if ($ arr_new_bad_ips_for_firewall -ne $ null) {
    # Escriba la fecha y la hora en el archivo de registro específico del script
    [DateTime] :: Ahora | Out-File -Append -coding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    # Escribir direcciones IP recién bloqueadas en el archivo de registro
    $ arr_new_bad_ips_for_firewall | Out-File -Append -Código utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

    # Boolean para asegurarse de que las nuevas IP solo se agreguen en una regla
    $ bln_added_to_rule = 0

    # Arreglo para mantener las IP incorrectas de cada regla de una en una, por lo que podemos contar para asegurarnos de que agregar las nuevas no supere las 1000 IP
    $ arr_existing_bad_ips_current_rule = @ ()

    # Para cada regla "BlockAttackers *" en el firewall, haga lo siguiente ...
    foreach ($ regla en $ arr_firewall_rules) {
        if ($ bln_added_to_rule -ne 1) {
            # Divida las IP existentes de la regla actual en una matriz para que podamos contarlas fácilmente
            $ arr_existing_bad_ips_current_rule = $ rule.RemoteAddresses -split (',')

            # Si el número de IP para agregar es menor que 1000 menos el número actual de IP en la regla, agréguelos a esta regla
            if ($ arr_new_bad_ips_for_firewall.Count -le (1000 - $ arr_existing_bad_ips_current_rule.Count)) {
                # Agregar nuevas direcciones IP a la regla de firewall
                $ arr_new_bad_ips_for_firewall | % {$ rule.RemoteAddresses + = ',' + $ _}

                # Escriba a qué regla se agregaron las IP al archivo de registro
                echo "Nuevas direcciones IP arriba agregadas a la regla del Firewall de Windows:" $ rule.Name | Out-File -Append -Código utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

                # Establezca booleano para que se omitan otras reglas al agregar IP
                $ bln_added_to_rule = 1
            }
        }
    }

    # Si no había espacio en ninguna otra regla de firewall "BlockAttackers *", cree una nueva y agréguele las IP
    if ($ bln_added_to_rule -ne 1) {
        $ str_new_rule_name = "BlockAttackers (Creado" + $ current_date_utc.ToString ("aaaa-MM-dd HH: mm: ss") + "UTC)"
        netsh advfirewall firewall agregar regla dir = en acción = nombre del bloque = $ str_new_rule_name description = "Regla creada automáticamente". enable = yes remoteip = "0.0.0.0" | Nulo
        $ new_rule = $ firewall.rules | Donde {$ _. Name -eq $ str_new_rule_name}

        # Agregar nuevas direcciones IP a la regla de firewall
        $ arr_new_bad_ips_for_firewall | % {$ new_rule.RemoteAddresses + = ',' + $ _}

        # Escriba a qué regla se agregaron las IP al archivo de registro
        echo "Nuevas direcciones IP arriba agregadas a la nueva regla de Firewall de Windows:" $ new_rule.Name | Out-File -Append -coding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    }
}

El nombre de la carpeta de registro del FTP FTPSVC*en la línea 54 debe completarse por causa. En la línea 115 y 116 se debe ingresar la IP de su servidor (IPv4 e IPv6), de lo contrario, la IP de los servidores propios podría agregarse a la regla del firewall cien veces. La variable $int_block_limitque configuro en 1 en mi servidor, por lo que el script está bloqueando un ataque de piratas informáticos que causa un evento 4625 en dos segundos. Todavía estoy pensando en ejecutar el script adicionalmente para que ocurran 4625 eventos en un período de tiempo de unos pocos minutos. De hecho, también sería posible separar las secuencias de comandos y dejar que una secuencia de comandos verifique los eventos de 4625 activados por el evento de 4625 y otra que compruebe periódicamente las carpetas de registro del FTP cada 5 o 10 minutos, incluso con una regla de firewall separada y archivo de registro.

Uwe Martens
fuente
-1

He agregado el mío para 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 $_;
    }

Luego tendrá que agregar la matriz a 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
fuente