Сервер с RDP на стандартном порту 3389 начинают перебирать через несколько минут после появления в интернете. Защита RDP от брутфорса складывается из двух частей: базовых настроек, которые уменьшают риск, и автоматической блокировки IP, с которых подбирают пароль. Ниже обе части по шагам, с готовым скриптом PowerShell и честным разбором его ограничений.
Базовые меры защиты RDP
- Не открывайте RDP в интернет, если можно. Лучший вариант: доступ через VPN или шлюз удалённых рабочих столов (RD Gateway). Тогда порт 3389 снаружи просто закрыт.
- Ограничьте доступ по IP. Если подключаются только из офиса, разрешите в правиле брандмауэра Windows для RDP только адреса офиса.
- Включите NLA (проверку подлинности на уровне сети). Без неё сервер показывает окно входа любому, кто подключился.
- Уберите простые пароли и переименуйте Administrator. Боты перебирают в первую очередь administrator, admin, user и похожие имена.
- Осторожно с политикой блокировки учётных записей. Она останавливает перебор, но блокирует и настоящего пользователя: атакующий, перебирая пароль, фактически отключает сотруднику доступ.
- Смена порта уменьшает шум от массовых сканеров, но не защищает: нестандартный порт находится обычным сканированием.
Если RDP всё же открыт в интернет, нужна автоматическая блокировка атакующих IP.
Где Windows записывает неудачные входы по RDP
Основной источник: журнал безопасности, событие 4625 («Не удалось выполнить вход в учётную запись»). В нём есть поле «Сетевой адрес источника» с IP атакующего и тип входа: 10 для RDP без NLA и 3 для входа с NLA.
У события 4625 есть две особенности:
- Оно не только про RDP. Тем же событием записываются неудачные входы по SMB и другим сетевым протоколам. Для защиты это скорее плюс: перебор через SMB тоже стоит блокировать.
- При NLA IP часто пустой. Вместо адреса стоит прочерк. Тогда IP нужно брать из журнала
Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational, событие 140: в его тексте указан IP клиента, который ввёл неверное имя пользователя или пароль.
Событие 4625 пишется, только если включён аудит неудачных входов. Проверить можно так (GUID работает и в русской, и в английской Windows):
auditpol /get /subcategory:"{0CCE9215-69AE-11D9-BED3-505054503030}"
В ответе у подкатегории входа в систему должен стоять «Сбой» (Failure). Если нет, включите:
auditpol /set /subcategory:"{0CCE9215-69AE-11D9-BED3-505054503030}" /failure:enable
Скрипт PowerShell: автоматическая блокировка IP при переборе RDP
Скрипт ниже собирает IP из событий 4625 и 140 за последние 2 часа, находит адреса с 5 и больше неудачными попытками и добавляет их в блокирующее правило брандмауэра Windows. Сохраните его как C:\Scripts\block-rdp-bruteforce.ps1:
# Блокирует IP, с которых за последние 2 часа было 5 и больше неудачных входов
$Threshold = 5
$Since = (Get-Date).AddHours(-2)
$RuleName = 'Block RDP brute-force'
$Allow = @('203.0.113.10') # внешние адреса офиса и VPN, которые нельзя блокировать
$IpRegex = '\b(\d{1,3}(?:\.\d{1,3}){3})\b'
# 1. Журнал безопасности, событие 4625: поле IpAddress (Properties[19])
$ips = @(Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=$Since} -ErrorAction SilentlyContinue |
ForEach-Object { $_.Properties[19].Value })
# 2. Журнал RdpCoreTS, событие 140: IP в тексте события (нужен при NLA, когда в 4625 прочерк)
$ips += Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational'; Id=140; StartTime=$Since} -ErrorAction SilentlyContinue |
ForEach-Object { if ($_.Message -match $IpRegex) { $Matches[1] } }
$bad = $ips |
Where-Object { $_ -match "^$IpRegex$" -and $_ -notin $Allow -and $_ -ne '127.0.0.1' } |
Group-Object | Where-Object Count -ge $Threshold |
Select-Object -ExpandProperty Name
if (-not $bad) { return }
$rule = Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue
if ($rule) {
$old = @(($rule | Get-NetFirewallAddressFilter).RemoteAddress)
$all = @($old + $bad | Sort-Object -Unique)
Set-NetFirewallRule -DisplayName $RuleName -RemoteAddress $all
} else {
New-NetFirewallRule -DisplayName $RuleName -Direction Inbound -Action Block `
-Protocol TCP -LocalPort 3389 -RemoteAddress $bad | Out-Null
}
Если событие 4625 и событие 140 пишутся на одну и ту же попытку, она посчитается дважды. С порогом 5 это значит, что бан наступит примерно после третьей попытки. Для защиты RDP это нормально.
Запуск по расписанию
Запускайте скрипт каждые 5 минут от имени SYSTEM:
schtasks /create /tn "Block RDP brute-force" /sc minute /mo 5 /ru SYSTEM /rl HIGHEST /tr "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\block-rdp-bruteforce.ps1"
Проверить, какие адреса уже заблокированы:
(Get-NetFirewallRule -DisplayName 'Block RDP brute-force' | Get-NetFirewallAddressFilter).RemoteAddress
Ограничения самописной защиты RDP
Скрипт закрывает базовую задачу, но у него есть предсказуемые слабые места:
- Баны вечные. Адрес никогда не удаляется из правила. Список растёт, а случайно заблокированный клиент остаётся заблокированным, пока вы не уберёте его вручную.
- Закрыт только RDP. Тот же IP продолжает перебирать MS SQL, почту или SMB, если они открыты.
- Каждый сервер сам по себе. Бот, забаненный на одном сервере, начинает всё сначала на следующем. На десятке серверов скрипт, whitelist и порог нужно поддерживать в десяти местах.
- Никто не узнает. Нет ни уведомлений, ни общей картины, кого и где заблокировали.
Защита RDP от брутфорса на всех серверах сразу
KIPBan делает то же самое, но без этих ограничений. Агент на Windows:
- читает события 4625 и журнал RdpCoreTS, а также журналы других служб: RD Web, MS SQL, MySQL, почтовых серверов, VNC, VPN/RRAS, OpenSSH для Windows;
- по умолчанию банит IP после 3 неудачных попыток подряд, если между ними прошло не больше 5 минут, и блокирует адрес на всех портах;
- снимает бан по сроку: 3 дня за первое нарушение, 30 дней за второе, дальше навсегда;
- не трогает адреса локальной сети и адреса из whitelist, в том числе подсети CIDR;
- сразу отправляет IP в общий бан-лист, и остальные ваши серверы, Windows и Linux, блокируют его в течение 5 минут;
- присылает уведомление в Telegram с именем сервера, IP и логином, под которым пытались войти.
Подробнее о том, как это устроено и чем отличается от других программ, в статье «Fail2ban для Windows». Все поля события 4625 и коды причин отказа описаны в документации Microsoft.
Вопросы про защиту RDP
Какой Event ID у неудачного входа по RDP?
В журнале безопасности это событие 4625, IP атакующего записан в поле «Сетевой адрес источника». Если включён NLA и в 4625 вместо адреса прочерк, IP есть в журнале RemoteDesktopServices-RdpCoreTS/Operational, событие 140.
Поможет ли смена порта RDP?
Частично. Смена порта отсекает часть массовых сканеров, но нестандартный порт находится обычным сканированием. Это дополнение к блокировке IP, а не замена.
Почему не включить блокировку учётных записей?
Её можно включить, но она блокирует пользователя, а не атакующего. Бот, перебирающий пароль к учётной записи сотрудника, фактически лишает его доступа. Блокировка по IP останавливает сам источник атаки.
Как разблокировать IP, заблокированный скриптом?
Уберите адрес из правила: получите текущий список через Get-NetFirewallAddressFilter, удалите из него адрес и запишите обратно командой Set-NetFirewallRule с параметром RemoteAddress. Чтобы адрес больше не блокировался, добавьте его в массив $Allow.