devnulli / EvlWatcher

a "fail2ban" style modular log file analyzer for windows
MIT License
398 stars 49 forks source link

Blacklist Pattern #118

Closed shmightworks closed 10 months ago

shmightworks commented 1 year ago

Is there a way to add a blacklist by pattern? I'm seeing a handful of ips where they're all coming from the same range of ip. It'll clean up the list if I can do this.

Thanks.

Nionor commented 11 months ago

I have handled something similar with a power-shell script, but it's more for cleaning up the config then to prevent coming attacks though. It goes through all blacklisted IPs in the config and if there are more then 10 in a group (where the 3 first digits are the same) it creates a range from first to last blacklisted IP in that group and add it to a separate firewall rule. Then write the rest back to the config.

I can share it if there is any interest.

shmightworks commented 11 months ago

@Nionor That would be great. Currently I need to manually do this, while it's not hard, it could get annoying overtime. Thanks.

Nionor commented 11 months ago

Sure here it is, have a look and change it to your needs. It should run in the same folder as the config from EvlWatcher but I would recommend making a copy of the config and try it out in a separate folder to test it out a bit first. Oh and it needs to run as Admin to be able to create firewall rules, maybe this can be changed in some way but I'm a noob when it comes to ps scripts and for me it's no problem needing Admin so I haven't looked into it.

I added some comments so hopefully it can be better understood, there is also some debug stuff left in.

# Function to group IP addresses by their first three numbers
function Group-IpByFirstThreeNumbers {
    param (
        [string[]]$IpList
    )

    $groupedIps = @{}

    foreach ($ipAddress in $IpList) {
        # Extract the first three numbers of the IP address (e.g., 192.168.1)
        $firstThreeNumbers = ($ipAddress -split '\.')[0..2] -join '.'

        # Check if the group already exists in the hashtable
        if ($groupedIps.ContainsKey($firstThreeNumbers)) {
            $groupedIps[$firstThreeNumbers] += $ipAddress
        } else {
            $groupedIps[$firstThreeNumbers] = @($ipAddress)
        }
    }

    return $groupedIps
}

function Write-GroupedIpsToFW {
    param (
        [hashtable]$GroupedIps,
        [string]$RuleName
    )

    $singleIps = @()
    $ipRanges = @((Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue | Get-NetFirewallAddressFilter).RemoteAddress)

    # Iterate through each group in the hashtable and sort them by the size of the group
    foreach ($group in $GroupedIps.GetEnumerator() | Sort-Object { $_.Value.Count } -Descending) {

        if ($group.Value.Count -gt 10) {

            $sorted = $group.Value | Sort-Object { [int64]($_ -replace '\.') }
            # Log IPs added to FW
            $sorted -join ";" | Out-File -FilePath "IPsAddedToFW.txt" -Append

            $ipRange = "$($sorted[0])-$($sorted[-1])"
            if (!$ipRanges) {
                New-NetFirewallRule -DisplayName $RuleName -Action Block -RemoteAddress $ipRange | Out-Null
                $ipRanges = @((Get-NetFirewallRule -DisplayName $RuleName | Get-NetFirewallAddressFilter).RemoteAddress)
            } else {                
                $ipRanges += $ipRange
                Write-Host $ipRange                
            }

        } else {
            foreach ($ip in $group.Value) {
                $singleIps += $ip
            }
        }
    }

    if ($ipRanges.Count -gt 1 ) {
        Set-NetFirewallRule -DisplayName $RuleName -RemoteAddress $ipRanges | Out-Null        
    }

    return $singleIps
}

#CD $PSScriptRoot
$stopWatch = [Diagnostics.Stopwatch]::StartNew()

$EvlWatcherConfig = "$PSScriptRoot\config.xml"
# Check if the file exists
if (Test-Path $EvlWatcherConfig -PathType Leaf) {

    # Make a backup of config
    Copy-Item -Path $EvlWatcherConfig -Destination "$($EvlWatcherConfig).bak"

    $xmlData = [xml](Get-Content $EvlWatcherConfig)
    $banlist = @()

    $banlistData = $xmlData.Config.Global.Banlist -split ';'

    foreach ($item in $banlistData | Where { $_ -ne "" }) {
            $banlist += $item
    }

    # Group the IP addresses by their first three numbers
    $groupedIps = Group-IpByFirstThreeNumbers -IpList $banlist

    # Write grouped IPs to firewall
    $returnedIPs = Write-GroupedIpsToFW -GroupedIps $groupedIps -RuleName "EvlWatcherRanges"

    # Sort non grouped IPs
    $sortedIps = $returnedIPs | Sort-Object {[System.Version]$_}

    # Join all and add ; at the end
    $xmlData.Config.Global.Banlist = "$($sortedIps -join ';');"

    # Save the updated XML back to the file
    $xmlData.Save($EvlWatcherConfig)

} else {
    Write-Host "The XML file does not exist at the specified path."
}
Write-Host "Runtime: $($stopWatch.Elapsed.TotalSeconds)s"
$stopWatch.Stop()

I hope the code block here don't mess up anything...

devnulli commented 10 months ago

so.. im closing that because Blacklist to the firewall would be to complicated anway (😄 )