Защита RDP сервера от перебора паролей с блокировкой IP

Защита RDP сервера от перебора паролей с блокировкой IP

На любом хосте Windows, напрямую подключённом к Интернету, с открытым наружу портом RDP периодически будут фиксироваться попытки удаленного перебора паролей. Для эффективной защиты стандартного RDP порта
3389
от перебора паролей и эксплуатации уязвимостей рекомендуется разместить RDP сервер за VPN или шлюзом RD Gateway. Если реализовать такую схему невозможно, нужно внедрять дополнительные средства для защиты RDP:

В этой статье мы настроим автоматическую блокировку IP адресов, с которых выполняются неудачные попытки RDP аутентификации.

PowerShell скрипт для блокировки IP адресов-источников RDP атак

При неудачной попытке аутентификации в Windows по RDP в журнале событий Security регистрируется событие с EventID 4625 (неудачный вход —
An account failed to log on
и
LogonType = 3
, см. статью Анализ RDP логов в Windows ). В описании события указано имя пользователя, под которым выполнялась попытка подключения и IP адрес источника.

Можно вывести список таких событий с неудачными попытками входа за последний час по с помощью PowerShell командлета Get-WinEvent:
$result = Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625; EndTime=(get-date).AddHours(-1)} | ForEach-Object {
$eventXml = ([xml]$_.ToXml()).Event
[PsCustomObject]@{
UserName = ($eventXml.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
IpAddress = ($eventXml.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text'

EventDate = [DateTime]$eventXml.System.TimeCreated.SystemTime
}
}
$result

Неудачные попытки RDP входа, виден подбор паролей и пользователей

С помощью PowerShell скрипта можно анализировать этот лог, и, если с конкретного IP адреса за последние 2 часа было зафиксировано более 5 неудачных попыток входа по RDP, IP адрес будет добавлен в блокирующее правило Windows Firewall.

# количество неудачных попыток входа с одного IP адреса, при достижении которого нужно заблокировать IP
$badAttempts = 5
# Просмотр лога за последние 2 часа
$intervalHours = 2
# Если в блокирующем правиле более 3000 уникальных IP адресов, создать новое правило Windows Firewall
$ruleMaxEntries = 3000
# номер порта, на котором слушает RDP
$RdpLocalPort=3389
# файл с логом работы PowerShell скрипта
$log = "c:psrdp_block.log"
# Список доверенных IP адресов, которые нельзя блокировать
$trustedIPs = @("192.168.1.100", "192.168.1.101","8.8.8.8")  
 $startTime = [DateTime]::Now.AddHours(-$intervalHours)
$badRDPlogons = Get-EventLog -LogName 'Security' -After $startTime -InstanceId 4625 |
    Where-Object { $_.Message -match 'logon type:s+(3)s' } |
    Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]}}
$ipsArray = $badRDPlogons |
    Group-Object -Property IpAddress |
    Where-Object { $_.Count -ge $badAttempts } |
    ForEach-Object { $_.Name }
# Удалить доверенные IP адреса 
$ipsArray = $ipsArray | Where-Object { $_ -notin $trustedIPs }
if ($ipsArray.Count -eq 0) {
    return
}
[System.Collections.ArrayList]$ips = @()
[System.Collections.ArrayList]$current_ip_lists = @()
$ips.AddRange([string[]]$ipsArray)
$ruleCount = 1
$ruleName = "BlockRDPBruteForce" + $ruleCount
$foundRuleWithSpace = 0
while ($foundRuleWithSpace -eq 0) {
    $firewallRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
    if ($null -eq $firewallRule) {
  New-NetFirewallRule -DisplayName $ruleName –RemoteAddress 1.1.1.1 -Direction Inbound -Protocol TCP –LocalPort $RdpLocalPort -Action Block
        $firewallRule = Get-NetFirewallRule -DisplayName $ruleName
        $current_ip_lists.Add(@(($firewallRule | Get-NetFirewallAddressFilter).RemoteAddress))
        $foundRuleWithSpace = 1
    } else {
        $current_ip_lists.Add(@(($firewallRule | Get-NetFirewallAddressFilter).RemoteAddress))        
        if ($current_ip_lists[$current_ip_lists.Count – 1].Count -le ($ruleMaxEntries – $ips.Count)) {
            $foundRuleWithSpace = 1
        } else {
            $ruleCount++
            $ruleName = "BlockRDPBruteForce" + $ruleCount
        }
    }
}
# Удалить IP адреса, которые уже есть в правиле 
for ($i = $ips.Count – 1; $i -ge 0; $i--) {
    foreach ($current_ip_list in $current_ip_lists) {
        if ($current_ip_list -contains $ips[$i]) {
            $ips.RemoveAt($i)
            break
        }
    }
}
if ($ips.Count -eq 0) {
    exit
}
# Заблокировать IP в firewall и записать в лог
$current_ip_list = $current_ip_lists[$current_ip_lists.Count – 1]
foreach ($ip in $ips) {
    $current_ip_list += $ip
    (Get-Date).ToString().PadRight(22) + ' | ' + $ip.PadRight(15) + ' | The IP address has been blocked due to ' + ($badRDPlogons | Where-Object { $_.IpAddress -eq $ip }).Count + ' failed login attempts over ' + $intervalHours + ' hours' >> $log
}
Set-NetFirewallRule -DisplayName $ruleName -RemoteAddress $current_ip_list
В русской версии Windows этот скрипт не будет работать, т.к. расчитан на ENG редакцию. Для адаптации скрипт под русскую Windows замените строку:

$badRDPlogons = Get-EventLog -LogName ...

На блок кода:

$ipPattern = 'b(?:d{1,3}.){3}d{1,3}b'
$badRDPlogons = @()
$events = Get-WinEvent -FilterHashTable @{LogName='Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational';ID='140';StartTime=$startTime}
$events.message | ForEach-Object {
if ($_ -match $ipPattern) {
$ipObject = [PSCustomObject]@{
IpAddress = $matches[0]
}
$badRDPlogons += $ipObject
}
}

После запуска PoweShell скрипт создаст правило в Windows Defender Firewall, которое заблокирует подключение к RDP порту для полученного списка IP адресов.

Правило Windows Defender Firewall для блокировки RDP доступа для IP адресов, с которых идет перебор паролей

В лог файл скрипт PowerShell будет записывать список заблокированных IP:

Лог заблокированных IP адресов с которых выполняется подбор RDP

Готовый скрипт block_rdp_attack.ps1 можно скачать с GitHub: https://github.com/winadm/posh/blob/master/RemoteDesktop/block_rdp_attack.ps1

Чтобы скачать PS1 скрипт с GitHub на локальный диск, выполните:

$path = "C:PS"
If(!(test-path -PathType container $path))
{
      New-Item -ItemType Directory -Path $path
}
$url = "https://raw.githubusercontent.com/winadm/posh/master/RemoteDesktop/block_rdp_attack.ps1"
$output = "$pathblock_rdp_attack.ps1" 
Invoke-WebRequest -Uri $url -OutFile $output

Можно запускать скрипт автоматически при появлении в логе события 4625. Такое задание планировщика можно создать с помощью PowerShell скрипта:
$taskname="BlockRDPBruteForce_Attack_PS"
$scriptPath="C:PSblock_rdp_attack.ps1"
$triggers = @()
$triggers += New-ScheduledTaskTrigger -AtLogOn
$CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger
$trigger = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly
$trigger.Subscription =
@"

"@
$trigger.Enabled = $True
$triggers += $trigger
$User='Nt AuthoritySystem'
$Action=New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-NoProfile -NoLogo -NonInteractive -ExecutionPolicy Bypass -File $scriptPath"
Register-ScheduledTask -TaskName $taskname -Trigger $triggers -User $User -Action $Action -RunLevel Highest -Force

задание планировщика для автоматического блокирования IP адресов с которых идут попытки RDP подключений с перебором паролей

Задание будет запускаться при каждом неудачном RDP входе, анализировать журнал событий и блокировать IP адреса. Задание запускается от имени System и не зависит от того, выполнени ли вход пользователем или нет.

Windows 10
Защита RDP сервера от перебора паролей с блокировкой IP