Загрузка данных


# ======================================================
# Название: Блокировка брутфорса VPN/RRAS (IAS/NPS)
# Описание: Событие 6273 (NPS). IP атакующего = CallingStationID.
#           При 5+ неудачных входах с IP - блокирует на 1 год.
# Запуск:  Каждые 5 минут через планировщик задач
# ======================================================

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

# --- НАСТРОЙКИ ---
$Threshold        = 5          # 5+ неудач с одного IP за период
$CheckMinutes     = 10
$BlockMinutes     = 525600     # 1 год
$FirewallRuleName = "Block-BruteForce-Powershell"
$WhiteList = @(
    "192.168.1.100",
    "10.0.0.50",
    "176.212.166.157",
    "26.175.174.142"   # ваш NAS — на всякий случай
)

# --- ЛОГИРОВАНИЕ ---
function Write-Log {
    param([string]$Message)
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Write-Host "[$timestamp] $Message"
}

function Test-IsPrivateIP {
    param([string]$IP)
    if ($IP -eq "127.0.0.1" -or $IP -eq "::1" -or $IP -eq "-") { return $true }
    if ($IP -match '^10\.') { return $true }
    if ($IP -match '^172\.(1[6-9]|2[0-9]|3[0-1])\.') { return $true }
    if ($IP -match '^192\.168\.') { return $true }
    if ($IP -match '^169\.254\.') { return $true }
    if ($IP -match '^26\.') { return $true }   # ваш диапазон NAS
    return $false
}

# --- ОСНОВНАЯ ЛОГИКА ---
Write-Log "Start checking (period: $CheckMinutes minutes)"

$now   = Get-Date
$start = $now.AddMinutes(-$CheckMinutes)

Write-Log "Checking 6273 events from $($start.ToString('yyyy-MM-dd HH:mm:ss')) to $($now.ToString('yyyy-MM-dd HH:mm:ss'))"

# --- ЧТЕНИЕ СОБЫТИЙ 6273 (NPS) ---
try {
    $events = Get-WinEvent -FilterHashtable @{
        LogName   = "Security"
        Id        = 6273
        StartTime = $start
        EndTime   = $now
    } -ErrorAction Stop
} catch {
    if ($_.Exception.Message -match "No events were found") {
        Write-Log "No 6273 events found (normal if no failed VPN logins)"
        exit 0
    } else {
        Write-Log "Error reading event log: $_"
        exit 1
    }
}

if (-not $events) {
    Write-Log "No 6273 events found"
    exit 0
}

Write-Log "Found events 6273: $($events.Count)"

# --- ПАРСИНГ СОБЫТИЙ ---
$ipList = New-Object System.Collections.Generic.List[object]

foreach ($event in $events) {
    try {
        # Быстрый доступ через Properties (индексы для 6273)
        # 0=SubjectUserSid, 1=SubjectUserName, 2=SubjectDomainName,
        # 3=FullyQualifiedSubjectUserName, 4=SubjectMachineSID,
        # 5=SubjectMachineName, 6=FullyQualifiedSubjectMachineName,
        # 7=MachineInventory, 8=CalledStationID, 9=CallingStationID,
        # 10=NASIPv4Address, 11=NASIPv6Address, 12=NASIdentifier,
        # 13=NASPortType, 14=NASPort, 15=ClientName,
        # 16=ClientIPAddress, 17=ProxyPolicyName, 18=NetworkPolicyName,
        # 19=AuthenticationProvider, 20=AuthenticationServer,
        # 21=AuthenticationType, 22=EAPType, 23=AccountSessionIdentifier,
        # 24=ReasonCode, 25=Reason, 26=LoggingResult
        $props = $event.Properties

        $user       = $props[1].Value
        $callingId  = $props[9].Value   # IP атакующего
        $reasonCode = $props[24].Value

        # Нам нужны только неудачи (ReasonCode 16 = неверные креды;
        # 0 = успех — пропускаем)
        if ([int]$reasonCode -eq 0) { continue }

        if ($callingId -and $callingId -ne "-") {
            $ipList.Add([PSCustomObject]@{
                IpAddress   = $callingId
                TargetUser  = $user
                TimeCreated = $event.TimeCreated
                ReasonCode  = $reasonCode
            })
        }
    } catch {
        continue
    }
}

if ($ipList.Count -eq 0) {
    Write-Log "No valid CallingStationID found in 6273 events"
    exit 0
}

Write-Log "Valid failed-auth events: $($ipList.Count)"

# --- ФИЛЬТРАЦИЯ ---
$filteredIPs = @()
foreach ($item in $ipList) {
    if ($WhiteList -contains $item.IpAddress) { continue }
    if (Test-IsPrivateIP -IP $item.IpAddress) { continue }
    $filteredIPs += $item
}

if ($filteredIPs.Count -eq 0) {
    Write-Log "All IPs are whitelisted or private"
    exit 0
}

Write-Log "After filtering: $($filteredIPs.Count) events"

# --- ГРУППИРОВКА ПО IP ---
$groupedIPs = @{}
foreach ($item in $filteredIPs) {
    if ($groupedIPs.ContainsKey($item.IpAddress)) {
        $groupedIPs[$item.IpAddress].Count++
        $groupedIPs[$item.IpAddress].Users += ", " + $item.TargetUser
        $groupedIPs[$item.IpAddress].LastAttempt = $item.TimeCreated
    } else {
        $groupedIPs[$item.IpAddress] = @{
            Count        = 1
            Users        = $item.TargetUser
            FirstAttempt = $item.TimeCreated
            LastAttempt  = $item.TimeCreated
        }
    }
}

# --- ОПРЕДЕЛЕНИЕ НАРУШИТЕЛЕЙ ---
$violators       = @()
$violatorDetails = @{}
foreach ($key in $groupedIPs.Keys) {
    if ($groupedIPs[$key].Count -ge $Threshold) {
        $violators       += $key
        $violatorDetails[$key] = $groupedIPs[$key]
    }
}

if ($violators.Count -eq 0) {
    Write-Log "No violators found (threshold: $Threshold)"
    exit 0
}

Write-Log "Found violators: $($violators.Count)"
foreach ($ip in $violators) {
    $d = $violatorDetails[$ip]
    Write-Log "  - $ip ($($d.Count) attempts in $CheckMinutes min, users: $(($d.Users -split ', ' | Select-Object -Unique) -join ','))"
}

# --- БЛОКИРОВКА В FIREWALL ---
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Log "ERROR: Administrator rights required!"
    exit 1
}

$rule = Get-NetFirewallRule -DisplayName $FirewallRuleName -ErrorAction SilentlyContinue

try {
    if ($rule) {
        Write-Log "Rule '$FirewallRuleName' found, updating..."

        $addressFilter = $rule | Get-NetFirewallAddressFilter -ErrorAction SilentlyContinue
        $currentIps = @()
        if ($addressFilter) { $currentIps = @($addressFilter.RemoteAddress) }

        # Объединяем старые + новые
        $allIps = @()
        foreach ($ip in ($currentIps + $violators)) {
            if ($ip -and $allIps -notcontains $ip) { $allIps += $ip }
        }

        $rule | Remove-NetFirewallRule -ErrorAction Stop

        New-NetFirewallRule -DisplayName $FirewallRuleName `
            -Direction Inbound `
            -Action Block `
            -Enabled True `
            -RemoteAddress $allIps `
            -Profile Any `
            -Description "Auto block brute-force (updated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'))" `
            -ErrorAction Stop

        Write-Log "Rule recreated. Total IPs: $($allIps.Count)"
    } else {
        Write-Log "Rule not found, creating new..."

        New-NetFirewallRule -DisplayName $FirewallRuleName `
            -Direction Inbound `
            -Action Block `
            -Enabled True `
            -RemoteAddress $violators `
            -Profile Any `
            -Description "Auto block brute-force (created: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'))" `
            -ErrorAction Stop

        Write-Log "New rule created. Blocked: $($violators.Count)"
    }
} catch {
    Write-Log "ERROR with firewall: $_"
    exit 1
}

# --- ЛОГИРОВАНИЕ В ФАЙЛ ---
$logDir = "C:\Logs"
if (-not (Test-Path $logDir)) {
    try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch {}
}

if (Test-Path $logDir) {
    $logFile = "$logDir\BlockedIPs_5min.log"
    foreach ($ip in $violators) {
        $d = $violatorDetails[$ip]
        $uniqueUsers = ($d.Users -split ', ' | Select-Object -Unique) -join ', '
        $logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | BLOCKED IP: $ip | Attempts: $($d.Count) in $CheckMinutes min | Users: $uniqueUsers"
        Add-Content -Path $logFile -Value $logMessage
    }
    Write-Log "Block info written to $logFile"
}

# --- ПРОВЕРКА ---
$verifyRule = Get-NetFirewallRule -DisplayName $FirewallRuleName -ErrorAction SilentlyContinue
if ($verifyRule) {
    $verifyIPs = @((Get-NetFirewallAddressFilter -AssociatedObject $verifyRule).RemoteAddress)
    Write-Log "Total IPs in rule: $($verifyIPs.Count)"
    foreach ($ip in $violators) {
        if ($verifyIPs -contains $ip) {
            Write-Log "  OK $ip - blocked"
        } else {
            Write-Log "  FAIL $ip - NOT found in rule!"
        }
    }
} else {
    Write-Log "Rule not found for verification!"
}

Write-Log "Blocked $($violators.Count) IPs for $BlockMinutes minutes (1 year)"
exit 0