Zablokować adres IP na podstawie liczby X nieudanych prób logowania?

47

Czy można zablokować adres IP po X nieudanych próbach logowania do systemu Windows Server? Nie do konkretnego konta, które wiem, jak to zrobić, ale do całej maszyny.

Uderzają nas dość brutalne ataki próbujące odgadnąć nazwy użytkowników, więc naprawdę pomogłoby to odciążyć serwer.

HeavyWave
źródło
8
* nix ma fial2ban ... nie jestem pewien, czy istnieje odpowiednik / port systemu Windows. fail2ban.org/wiki/index.php/Main_Page
Chris Nava
5
Od Evana Andersona: serverfault.com/questions/43360/ ... ... wydaje się być dobrym odpowiednikiem funkcjonalności fail2ban, ale ponieważ twoje pytanie nie jest wystarczająco szczegółowe, nie wiem, czy chcesz zablokować adresy IP próbujące się zalogować do hostowanej witryny, serwera (przez SSH) lub domeny. Wyjaśnienie poszedłoby daleko. Dodatkowo możesz ustalić limit prędkości na swojej zaporze ogniowej, ale to zależy od implementacji.
4
Być może warto zajrzeć na serverfault.com/questions/216995/... w celu wcześniejszej dyskusji na temat przydatności automatycznego banowania na podstawie adresu IP.
pehrs
1
Jeśli mówisz o usługach terminalowych / pulpicie zdalnym, spójrz tutaj: serverfault.com/a/335976/7200
Evan Anderson
3
Zrobiłem usługę Windows na github, aby to zrobić: github.com/jjxtra/Windows-IP-Ban-Service
jjxtra

Odpowiedzi:

28

Możesz to zrobić za pomocą programu PowerShell i menedżera zadań. Prawdopodobnie nie jest to idealne rozwiązanie, ale działa całkiem dobrze i mam około 100 zablokowanych adresów IP w ciągu dwóch miesięcy. Napisałem skrypt, który wybiera z określonych zdarzeń EventLog („niepowodzenie audytu”). Jeśli istnieje wiele nieudanych prób logowania z dowolnego adresu IP, jest on dodawany do reguły zapory (utworzonej ręcznie) o nazwie „BlockAttackers”, która blokuje wszelki ruch do określonych adresów IP.

Skrypt 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

Utwórz zadanie w harmonogramie i ustaw wyzwalacz na zdarzenie 4625 (logowanie do systemu Windows, w tym usługi terminalowe). Możesz jednak ustawić wyzwalacz, aby działał np. Dwa razy na godzinę, aby uniknąć niepotrzebnego ładowania serwera.

Wyzwalacz harmonogramu

a po uruchomieniu uruchom skrypt PowerShell. Musisz również ustawić wyższe uprawnienia, aby uruchomić ten skrypt, w przeciwnym razie zakończy się niepowodzeniem z wyjątkiem wyjątku bezpieczeństwa.

uruchamianie skryptu PowerShell

Możesz także powiązać ten skrypt z innymi zdarzeniami bezpieczeństwa.

remunda
źródło
1
Doskonały skrypt @remunda - dzięki! Otrzymywałem też dużo 4625s z FTP, dla których dziennik bezpieczeństwa nie ma adresów IP, więc rozwinąłem twój skrypt, aby sprawdził również dziennik FTP z bieżącego dnia. Proszę zobaczyć moją odpowiedź poniżej, aby uzyskać więcej informacji: serverfault.com/a/571903/107701
kevinmicke
Istnieje wiele gotchas i Edge'ów z dziennikami zdarzeń, logowaniem adresu IP itp., Które obsługiwałem w IPBan - darmowym i otwartym oprogramowaniu na github.com/jjxtra/Windows-IP-Ban-Service
jjxtra
7

Wiem, że to pytanie jest stare, ale tak naprawdę był to pierwszy post na forum, na który natknąłem się, kiedy kilka tygodni temu zacząłem robić dokładnie to samo. Udało mi się znaleźć działający skrypt, który analizuje dzienniki zdarzeń 24 godziny wstecz tylko w przypadku złych wpisów w dzienniku zdarzeń logowania, chwytam te, które mają więcej niż 10 złych danych logowania, a następnie umieszczam je na liście filtrów ipsec przy użyciu polecenie netsh. Następnie napisałem plik wsadowy z tym wierszem powershell .\*scriptname.ps1*i utworzyłem zaplanowane zadanie, aby uruchamiać plik wsadowy co 24 godziny (z jakiegoś powodu nie można go wykonać bezpośrednio).

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

Wiem, że ten skrypt jest prawdopodobnie nieefektywny, ale kiedy zacząłem nad tym pracować, nie miałem absolutnie żadnego doświadczenia w PowerShell, więc moja zdolność do optymalizacji skryptów pozostawia wiele do życzenia. Jednak pomimo tego faktu pomyślałem, że podzielę się tym z każdym, kto mógłby z niego skorzystać.

Dziękuję Remundi za pomysł, że ten plakat sprawił, że wpadłem na pomysł użycia PowerShell do przeszukiwania dzienników zdarzeń.

Keegan
źródło
4

Ten skrypt bazuje na odpowiedzi remunda i idzie nieco dalej https://serverfault.com/a/397637/155102 Uwzględnia regułę „BlockAttackers”, która nie ma jeszcze żadnych adresów IP (co zwraca ciąg „*”). Zapisuje również komentarz do pliku dziennika, aby poinformować Cię, kiedy adres IP został dodany do reguły.

Dobrą wskazówką jest utworzenie reguły „BlockAttackers”, która blokuje adresy IP, ALE najpierw musi być wyłączona. Następnie uruchom ten skrypt raz ręcznie, aby wypełnić pole „Adresy zdalne” rzeczywistymi adresami IP, które powinny zostać zablokowane. Spójrz na te adresy IP, aby upewnić się, że nie dodano nic krytycznego, a następnie włącz regułę zapory. Dodaj tę regułę do zapory sieciowej zgodnie z opisem remunda.

Git dla tego skryptu

#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
źródło
2

Zasadniczo nie jest dobrym pomysłem, aby pozwolić komuś innemu na kontrolowanie reguł zapory. Właśnie o to tutaj prosisz.

Thorsten
źródło
1
+1, jest to doskonały sposób na przygotowanie się do ataku typu „odmowa usługi”. A jeśli używasz ograniczających szybkość, większość zautomatyzowanych narzędzi brutalnej przestrzeni umieszcza ich próby logowania wystarczająco daleko od siebie, aby uniknąć złapania.
12
Automatyczne blokowanie adresów IP po określonej liczbie nieudanych prób logowania jest bardzo powszechną praktyką. Widzę, że hosty są blokowane co godzinę po próbie odgadnięcia haseł FTP. Jedynym sposobem, w jaki może to być atak DoS, jest to, że komuś udało się sfałszować twoje IP (niemożliwe w połączeniach TCP) lub jeśli wielokrotnie błędnie wpisujesz hasło (w takim przypadku to nie ktoś inny kontroluje reguły zapory, to ty)
devicenull
18
Przepraszam, ale nie zapytałem, czy to dobry pomysł.
HeavyWave
1
Oczywiście nie ma powodu, dla którego nie można było ustawić wyjątków dla jednego lub więcej określonych adresów IP, co praktycznie wyeliminowałoby problem DoS.
John Gardeniers,
2

To jest stary wątek. Korzystałem ze skryptu dostarczonego przez kevinmicke w latach 2014-2015. Potem przestało działać. Musiałem więc go trochę edytować, aby zastosować uwierzytelnianie Windows Network Security, które nie pozostawia adresów IP w dzienniku bezpieczeństwa. Ponadto, ponieważ nie mam zwykłego FTP, usunąłem tę część, ponieważ powodowała błędy, ponieważ nie było folderu dziennika. Główna zmiana dotyczy źródła zdarzeń 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
        }
    }

Powyższy skrypt będzie działał w systemie Windows 2012. Jeśli nadal używasz Pulpitu zdalnego z uwierzytelnianiem na poziomie dostępu do sieci w systemie Windows 2008, może być konieczne wykonanie następującej sztuczki. System Windows 2008 nie ma adresów IP w dzienniku zabezpieczeń i wydaje się, że nie ma ich również w dzienniku Microsoft-Windows-RemoteDesktopServices-RdpCoreTS. Musiałem więc użyć 2 dzienników - dopasować zdarzenia z dziennika bezpieczeństwa do udanych prób dostępu do portu 3389 w dzienniku zapory. To zgadywanka, ale wydaje się, że wykrywa ataki hasłem. Oto część, która gromadzi naruszające adresy IP:

    $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

UWAGA: Nie zapomnij włączyć dzienników zapory. UWAGA 2: Nie jestem ekspertem od PowerShell, więc byłoby miło, gdyby niektórzy guru mogli poprawić / poprawić mój kod.

Uspokój się
źródło
1

Używam ts_block freeby.

Zasadniczo jest to „program VBScript, który działa jako ujście zdarzeń WMI, odbierając zdarzenia zarejestrowane przez system Windows w odpowiedzi na nieprawidłowe logowanie do usług terminalowych”.

Wygląda na to, że działa idealnie, a skrypt jest prosty, jeśli trzeba go zmodyfikować. Możesz albo pozwolić mu rejestrować próby, a następnie banować na podstawie liczby dozwolonych prób i / lub możesz na stałe wpisać nazwy logowania, do których nie chcesz dać dostępu.

Zostałem złapany przez przypadkowe dodanie tej samej nazwy dwa razy, a usługa przechodzi w niekończącą się pętlę restartującą się co 1500 ms, ale bardzo łatwo ją naprawić / zmodyfikować, jeśli nie masz nic przeciwko VBS.

Moje obecne ustawienia to tylko jedna ponowna próba i jesteś zbanowany na 2 dni, a loginy takie jak „admin”, „administrator”, „administrator”, „gość” itp. Są automatycznie blokowane. Czy zmiana adresu IP powinna być prosta?

Trochę uzależniające, aby wejść i zobaczyć, które stworzenia zostały zakazane na noc ...

pluskwa
źródło
0

Masz na myśli logowanie do serwera / domeny lub logowanie do strony internetowej działającej na serwerze? Jeśli masz na myśli logowanie do serwera / domeny, odpowiedź brzmi: nie. System Windows nie ma pojęcia blokowania adresów IP na podstawie nieudanych prób logowania, ponieważ adresy IP nie są jednostkami bezpieczeństwa. Mogą istnieć narzędzia innych firm, które mogą to zrobić, ale nie jestem świadomy żadnego z nich, ponieważ nigdy tego nie szukałem.

joeqwerty
źródło
0

Jeśli atakowany jest serwer WWW, możesz zainstalować rozszerzenie dynamicznych ograniczeń IP . Jeśli dotyczy to standardowego uwierzytelniania na serwerze, powinieneś być w stanie wdrożyć izolację domeny i serwera, co ograniczy zakres ataków do komputerów przyłączonych do domeny i może być ustawione tak, aby zezwalało tylko na próby z systemów, do których musisz mieć dostęp. serwer. W systemie Windows zapobieganie atakom siłowym polega na ustawieniu zasady blokowania konta na wartość 10 minut, a zasady złego hasła na 3 próby - oznacza to, że atakowane konto zostanie zablokowane na 10 minut po 3 próbach. Połączenia IP domyślnie nie są blokowane w systemie Windows. (Nawiasem mówiąc, jestem również ciekawy, ile prób logowania na sekundę ma wpływ na system)

Jim B.
źródło
W mojej małej instancji AWS 1 próba co 4 sekundy wystarcza na zużycie 50% procesora. Całkiem bzdura, jeśli mnie o to poprosisz ...
RomanSt
Wow, zastanawiam się, dlaczego wykorzystanie jest tak duże. Będę musiał wykonać kilka testów, aby zobaczyć, ile prób zarejestruje się na moich maszynach wirtualnych.
Jim B
Myślę, że dzieje się tak dlatego, że nie używam uwierzytelniania na poziomie sieci, więc każda próba logowania powoduje wykrycie interfejsu użytkownika, który faktycznie przedstawia atakującemu za pośrednictwem sesji pulpitu zdalnego. Rozumiem, dlaczego to może być drogie.
RomanSt
0

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

Jeśli potrzebujesz gotowego rozwiązania (Zainstaluj i gotowe), możesz tutaj znaleźć bezpłatne narzędzie i prawdopodobnie powinieneś przeczytać dalej:

Aktualna wersja: 1.2 (.NET Framework 4.0 Client Profile) -> Pobierz aktualną wersję EvlWatcher (darmowy do użytku osobistego i komercyjnego)

Nowości w wersji 1.2 (więcej informacji w dokumentacji):

  • Konsola zarządzania
  • Wzorzec usługi WCF
  • Czarna lista
  • Automatyczne przejście do czarnej listy po 3 ostrzeżeniach (domyślnie)

Dla starszych serwerów (.NET Framework 2.0)

-> Pobierz zmniejszoną wersję EvlWatcher (darmowy do użytku osobistego i komercyjnego)

Mastro
źródło
0

Używając świetnego skryptu remunda jako punktu wyjścia, dodałem jedną ważną rzecz, której brakowało: blokowanie adresów IP przed nieudanymi logowaniami FTP . System Windows Server nie rejestruje adresu IP w dzienniku zabezpieczeń, gdy ktoś nie zaloguje się za pośrednictwem FTP, ale zamiast tego ustawia „Źródłowy adres sieciowy” na myślnik. FTP jest bardzo częstym wektorem ataku w przypadku ataków typu brute force, dlatego do jego skryptu dodałem możliwość skanowania dzienników FTP bieżącego dnia w poszukiwaniu wielu błędów logowania oraz blokowania również tych adresów IP.

Aktualizacja 2014/02/07: Kiedy wprowadziłem kilka drobnych poprawek do tego, aby przetwarzać wszystkie moje stare dzienniki FTP, zdałem sobie sprawę, że kiedy mieli ogromną liczbę prób (50 000+), utworzone tablice byłyby ogromne i sprawiłyby, że przetwarzanie było niesamowicie wolne. Od tamtej pory przepisałem go, aby uczynić go bardziej wydajnym podczas przetwarzania dzienników FTP.

Dowiedziałem się również, że istnieje arbitralny twardy limit 1000 adresów IP w jednej regule Zapory systemu Windows. Z powodu tego limitu potrzebowałem go, aby automatycznie utworzyć nową regułę, gdy najnowsza zapełni się. Teraz to robi, a także tworzy początkową regułę zapory (jeśli nie tworzysz własnej), więc jedyną możliwą konfiguracją jest dodanie jej do harmonogramu, aby działała, gdy wystąpi zdarzenie 4625.

Oto kod, który został przetestowany zarówno w systemie Windows Server 2008 R2, jak i 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
źródło
FYI: Dla tych, którzy wcześniej nie uruchomili skryptu PowerShell w systemie, musisz najpierw otworzyć nowy PowerShell i uruchomić Set-ExecutionPolicy RemoteSigned, abyś mógł uruchamiać skrypty lokalne. W przeciwnym razie pojawi się błąd: „Nie można załadować pliku blockattackers.ps1, ponieważ wykonywanie skryptów jest wyłączone w tym systemie”.
kevinmicke
0

Skrypt remuda , edytowany przez kevinmicke (7 lutego o 21:59), nie sprawdził kanału kontrolnego FTP, który ma własny folder w moim systemie (Windows Server 2008 R2). Ponadto 530 11001nie rozpoznano zdarzeń, które wydają się pojawiać, gdy haker próbuje jedynie uzyskać dostęp do kanału kontrolnego. Dołączyłem więc kilka wierszy w skrypcie, aby sprawdzić drugi folder dziennika FTP:

# Ten skrypt Windows PowerShell automatycznie blokuje adresy IP, które próbują zalogować się do systemu
# i nie uda się ustawić liczby razy poniżej przy pomocy zmiennej $ int_block_limit lub więcej. Skanuje zarówno zabezpieczenia
# log, który obejmuje Pulpit zdalny i inne próby, a także dziennik FTP z bieżącego dnia. Jeśli $ int_block_limit
# limit zostanie osiągnięty w jednym z tych dzienników (osobno, nie połączone), następnie adres IP zostanie dodany do
# reguła zapory.
#
# Skrypt automatycznie utworzy regułę zapory o nazwie „BlockAttackers (Utworzono rrrr-MM-dd GG: mm: ss UTC)” przy użyciu
# bieżący czas, jeśli taki o nazwie zawierającej „BlockAttackers” jeszcze nie istnieje. Ponieważ jest trudny
# limit 1000 wpisów (adresów IP), które można zablokować dla każdej reguły, spowoduje to również utworzenie reguł o podobnych nazwach
Limit został osiągnięty dla najnowszego.
#
# Polecam ustawienie skryptu, aby działał jako zaplanowane zadanie wywołane niepowodzeniem kontroli logowania zdarzenia 4625 z poziomu
# Dziennik bezpieczeństwa lub możesz ustawić, aby działał po pewnym czasie (tj. Co 10 minut).
#
# Autorzy:
# Większość skryptów napisanych przez użytkownika serverfault.com kevinmicke
# Część dziennika bezpieczeństwa systemu Windows napisana przez remunda serverfault.com, która stanowiła punkt wyjścia dla kevinmicke
# Sprawdzanie kanału kontrolnego FTP dodane przez użytkownika serverfault.com Uwe Martens
#
# Szczegóły: https://serverfault.com/questions/233222/ban-ip-address-based-on-x-number-of-unsuccessful-login-attempts


# Ustaw liczbę nieudanych prób logowania, po których adres IP zostanie zablokowany
$ int_block_limit = 3

# Okno czasu, w którym można sprawdzić dziennik zabezpieczeń, który jest obecnie ustawiony tak, aby sprawdzał tylko ostatnie 24 godziny
$ dat_time_window = [DateTime] :: Now.AddDays (-1)

# Wybierz z dziennika zabezpieczeń wszystkie adresy IP, które zawierają więcej niż $ niepowodzeń kontroli int_block_limit (zdarzenie 4625) w obrębie $ dat_time_window
$ arr_new_bad_ips_security_log = @ ()
$ arr_new_bad_ips_security_log = Get-EventLog -LogName 'Security' -InstanceId 4625 -Po $ dat_time_window |
    Select-Object @ {n = 'IpAddress'; e = {$ _. ReplacementStrings [-2]}} |
    Grupa-obiekt-właściwość IpAddress |
    Gdzie {$ _. Count -ge $ int_block_limit} |
    Wybierz -property Nazwa

# Uzyskaj aktualny czas UTC, aby ustalić nazwę pliku dla bieżącego dziennika FTP
$ current_date_utc = (Get-Date) .ToUniversalTime ()

# Ustaw ścieżkę do dzisiejszego pliku dziennika kanału kontrolnego FTP
$ str_log_file_file_name_control_channel = "C: \ inetpub \ logs \ LogFiles \ FTPSVC \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Wyszukaj w pliku dziennika kanału kontroli FTP „530 1”, aby znaleźć linie zawierające adresy IP systemów, które się nie zalogowały,
# pobierz tylko adres IP z każdej linii, pogrupuj adresy IP według adresów IP, aby policzyć próby z każdej z nich, i wybierz tylko
# Adresy IP, które mają $ int_block_limit lub więcej złych logowań dzisiaj
$ arr_new_bad_ips_ftp_control_channel = @ ()
$ arr_new_bad_ips_ftp_control_channel = Wybierz łańcuch $ str_log_file_file_nazwa_kontrolny_kanału - wzór „530 1” |
    ForEach-Object {$ _. Line.Substring (20,15) -replace ". *", ""} |
    Grupa |
    Gdzie {$ _. Count -ge $ int_block_limit} |
    Wybierz -property Nazwa

# Ustaw ścieżkę do dzisiejszego pliku dziennika FTP
$ str_log_file_name = "C: \ inetpub \ logs \ LogFiles \ FTPSVC * \ u_ex" + $ current_date_utc.ToString ("yyMMdd") + ".log"

# Wyszukaj w dzisiejszym pliku dziennika FTP wartość „530 1”, aby znaleźć linie zawierające adresy IP systemów, które się nie zalogowały,
# pobierz tylko adres IP z każdej linii, pogrupuj adresy IP według adresów IP, aby policzyć próby z każdej z nich, i wybierz tylko
# Adresy IP, które mają $ int_block_limit lub więcej złych logowań dzisiaj
# W FTPSVC * należy dodać identyfikator serwera FTP zamiast *, lub po prostu weź odpowiedni folder dziennika
$ arr_new_bad_ips_ftp = @ ()
$ arr_new_bad_ips_ftp = Wybierz łańcuch $ str_log_file_name -pattern "530 1" |
    ForEach-Object {$ _. Line.Substring (20,15) -replace ". *", ""} |
    Grupa |
    Gdzie {$ _. Count -ge $ int_block_limit} |
    Wybierz -property Nazwa

# Połącz dwie tablice adresów IP (jedną z dziennika bezpieczeństwa, drugą z dziennika 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)

# Sortuj tablicę, wybierając tylko unikalne adresy IP (w przypadku gdy jeden adres IP pojawia się zarówno w dziennikach zabezpieczeń, jak i FTP)
$ arr_new_bad_ips_all_sorted = @ ()
$ arr_new_bad_ips_all_sorted = $ arr_new_bad_ips_all |
    Foreach-Object {[ciąg] $ _. Nazwa} |
    Select-Object -unique

# Uzyskaj obiekt zapory ogniowej
$ firewall = New-Object -comobject hnetcfg.fwpolicy2

# Pobierz wszystkie reguły zapory pasujące do „BlockAttackers *”
$ arr_firewall_rules = $ firewall.Rules | Gdzie {$ _. Podobne do nazwy „BlockAttackers *”}

# Jeśli nie istnieje jeszcze reguła zapory „BlockAttackers *”, utwórz ją i ustaw na zmienną
if ($ arr_firewall_rules -eq $ null) {
    $ str_new_rule_name = "BlockAttackers (Utworzono" + $ current_date_utc.ToString ("rrrr-MM-dd GG: mm: ss") + „UTC)"
    netsh advfirewall firewall dodaj regułę katalog = w akcji = nazwa bloku = $ str_new_rule_name description = "Reguła automatycznie utworzona." enable = tak remoteip = "0.0.0.0" | Out-Null
    $ arr_firewall_rules = $ firewall.Rules | Gdzie {$ _. Podobne do nazwy „BlockAttackers *”}
}

# Podziel istniejące adresy IP z obecnych reguł zapory „BlockAttackers *” na tablicę, abyśmy mogli je łatwo wyszukać
$ arr_existing_bad_ips = @ ()
foreach ($ reguła w $ arr_firewall_rules) {
    $ arr_existing_bad_ips + = $ rules.RemoteAddresses -split (',')
}

# Wyczyść maski podsieci z adresów IP, które są obecnie blokowane przez reguły zapory
$ arr_existing_bad_ips_without_masks = @ ()
$ arr_existing_bad_ips_without_masks = $ arr_existing_bad_ips | ForEach-Object {$ _ -replace "/.*", ""}

# Wprowadź adres IP swojego serwera (IPv4 i IPv6) w wierszach 115 i 116.
# Wybierz adresy IP, które chcesz dodać do zapory, ale tylko te, które ...
$ arr_new_bad_ips_for_firewall = @ ()
$ arr_new_bad_ips_for_firewall = $ arr_new_bad_ips_all_sorted | Gdzie {
    # zawierają adres IP (tzn. nie są puste ani myślnik, który ma dziennik zabezpieczeń dla systemów, w których logowanie się nie powiodło)
    $ _. Długość -gt 6 -i
    # nie ma jeszcze w regułach zapory
    ! ($ arr_existing_bad_ips_without_masks -contains $ _) -i
    # nie są lokalnymi pętlami zwrotnymi
    ! ($ _. StartsWith ('127.0.0.1')) - i
    # nie są częścią lokalnej podsieci
    ! ($ _. StartsWith ('192.168.')) -I
    ! ($ _. StartsWith ('0.0.')) - i
    ! ($ _. StartsWith ('10 .0. ')) -I
    ! ($ _. StartsWith ('*. *. *. *')) - i
    ! ($ _. StartsWith ('*: *: *: *: *: *'))
}

# Jeśli istnieją adresy IP do zablokowania, wykonaj następujące czynności ...
if ($ arr_new_bad_ips_for_firewall -ne $ null) {
    # Wpisz datę i godzinę do pliku dziennika specyficznego dla skryptu
    [DateTime] :: Teraz | Plik wyjściowy -Załóż -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    # Napisz nowo zablokowane adresy IP do pliku dziennika
    $ arr_new_bad_ips_for_firewall | Plik wyjściowy -Załóż -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

    # Boolean, aby upewnić się, że nowe adresy IP są dodawane tylko do jednej reguły
    $ bln_added_to_rule = 0

    # Tablica do przechowywania złych adresów IP z każdej reguły pojedynczo, więc możemy liczyć, aby upewnić się, że dodanie nowych nie przekroczy 1000 adresów IP
    $ arr_existing_bad_ips_current_rule = @ ()

    # Dla każdej reguły „BlockAttackers *” w zaporze wykonaj następujące czynności ...
    foreach ($ reguła w $ arr_firewall_rules) {
        if ($ bln_added_to_rule -ne 1) {
            # Podziel istniejące adresy IP z bieżącej reguły na tablicę, abyśmy mogli je łatwo policzyć
            $ arr_existing_bad_ips_current_rule = $ rules.RemoteAddresses -split (',')

            # Jeśli liczba adresów IP do dodania jest mniejsza niż 1000 minus bieżąca liczba adresów IP w regule, dodaj je do tej reguły
            if ($ arr_new_bad_ips_for_firewall.Count -le (1000 - $ arr_existing_bad_ips_current_rule.Count)) {
                # Dodaj nowe adresy IP do reguły zapory
                $ arr_new_bad_ips_for_firewall | % {$ rules.RemoteAddresses + = ',' + $ _}

                # Napisz, która reguła została dodana do pliku dziennika
                echo "Nowe adresy IP powyżej dodane do reguły Zapory systemu Windows:" $ rules.Name | Plik wyjściowy -Załóż -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt

                # Ustaw wartość logiczną, aby wszelkie inne reguły były pomijane podczas dodawania adresów IP
                $ bln_added_to_rule = 1
            }
        }
    }

    # Jeśli w żadnej innej zaporze „BlockAttackers *” nie ma miejsca, utwórz nową i dodaj do niej adresy IP
    if ($ bln_added_to_rule -ne 1) {
        $ str_new_rule_name = "BlockAttackers (Utworzono" + $ current_date_utc.ToString ("rrrr-MM-dd GG: mm: ss") + „UTC)"
        netsh advfirewall firewall dodaj regułę katalog = w akcji = nazwa bloku = $ str_new_rule_name description = "Reguła automatycznie utworzona." enable = tak remoteip = "0.0.0.0" | Out-Null
        $ new_rule = $ firewall.rules | Gdzie {$ _. Nazwa -eq $ str_new_rule_name}

        # Dodaj nowe adresy IP do reguły zapory
        $ arr_new_bad_ips_for_firewall | % {$ new_rule.RemoteAddresses + = ',' + $ _}

        # Napisz, która reguła została dodana do pliku dziennika
        echo "Dodano nowe adresy IP powyżej do nowo utworzonej reguły Zapory systemu Windows:" $ new_rule.Name | Plik wyjściowy -Załóż -Encoding utf8 C: \ inetpub \ logs \ LogFiles \ blockattackers.txt
    }
}

Nazwa folderu dziennika FTP w FTPSVC*linii 54 musi zostać wypełniona z przyczyny. W wierszach 115 i 116 należy wprowadzić adres IP twojego serwera (IPv4 i IPv6), w przeciwnym razie adres IP własnego serwera może zostać dodany do reguły zapory sto razy. Zmienną $int_block_limitustawiam na 1 na moim serwerze, więc skrypt blokuje atak hakerów powodujący zdarzenie 4625 w ciągu dwóch sekund. Nadal zastanawiam się nad uruchomieniem skryptu oprócz wystąpienia 4625 zdarzeń w ciągu kilku minut. Oczywiście możliwe byłoby również oddzielenie skryptów i pozwolenie, by jeden skrypt sprawdzał zdarzenia 4625 wywołane przez zdarzenie 4625, a drugi sprawdzał foldery dziennika FTP co 5 lub 10 minut, nawet przy oddzielnej regule zapory i plik dziennika.

Uwe Martens
źródło
-1

Dodałem mój do 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 $_;
    }

Następnie musisz dodać tablicę do 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
źródło