Защита RDP: как закрыть Windows Server от атак

Опубликовано 14 мин. чтения

Свежеустановленный Windows-сервер собирает тысячи неудачных попыток входа по RDP за считаные часы. Это руководство показывает, какие меры действительно работают, чего стоит каждая и как не отрезать доступ себе самому.

Windows-сервер с открытым портом 3389 атакуют не когда-нибудь, а в течение первых минут. Сканеры работают непрерывно, они знают каждый диапазон адресов IPv4 и тупо перебирают имена пользователей и пароли. Почти каждый случай заражения шифровальщиком на небольших серверах начинается ровно в этой точке: учётная запись с именем Administrator, пароль, который кто-то счёл достаточным, и отсутствие блокировки после тысячной неудачной попытки.

Это руководство разбирает меры в порядке их реальной эффективности, а не в том порядке, в каком они стоят в большинстве статей. Все команды выполняются в сеансе PowerShell с правами администратора на Windows Server версий с 2016 по 2025.

Почему RDP остаётся самой атакуемой дверью

RDP не плохой протокол. Проблема в том, что он выставляет полноценную форму входа в открытый интернет и что Windows по умолчанию разрешает заполнять эту форму неограниченное число раз. Атакующему не нужна уязвимость, ему нужны только терпение и список паролей.

Насколько сильно затронут ваш сервер, видно из одной строки. Посчитайте неудачные попытки входа за последние 24 часа:

Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-1)} -ErrorAction SilentlyContinue | Measure-Object | Select-Object -ExpandProperty Count

Внутренний сервер без доступа из интернета обычно даёт несколько десятков событий в сутки, в основном это забытые пароли и старые служебные учётные записи. Сервер с открытым 3389 быстро выходит на четырёх- и пятизначные числа. Это число и есть ваша точка отсчёта: после описанных ниже мер оно должно упасть на порядки. Если Get-WinEvent отвечает сообщением «Не найдено событий, соответствующих указанным условиям выбора», значит ваш сервер вообще не пишет журнал неудачных входов. Это мы исправим ниже.

До первого изменения: обеспечьте себе путь назад

Любая из следующих мер может отрезать вам доступ. На сервере, к которому вы подключаетесь только по RDP, это конец сеанса и начало долгого вечера. Три предосторожности стоят пяти минут и предотвращают ровно это.

Во-первых: проверьте, есть ли у вас консоль, работающая независимо от RDP. У KVM root-серверов KernelHost в личном кабинете есть VNC-консоль, которая смотрит прямо на экран виртуальной машины. Она продолжает работать даже тогда, когда файрвол, сеть и служба RDP сломаны одновременно. Откройте её один раз для пробы до того, как что-то менять, а не после.

Во-вторых: заведите вторую учётную запись администратора, чтобы заблокированный логин не означал потерянный сервер. Имена групп зависят от языка системы, поэтому мы работаем с фиксированными идентификаторами SID (S-1-5-32-544 — локальная группа администраторов, S-1-5-32-555 — пользователи удалённого рабочего стола):

New-LocalUser -Name 'kh-adm' -Password (Read-Host -AsSecureString -Prompt 'Пароль') -FullName 'Сервисная учётная запись' -PasswordNeverExpires
Add-LocalGroupMember -SID 'S-1-5-32-544' -Member 'kh-adm'
Add-LocalGroupMember -SID 'S-1-5-32-555' -Member 'kh-adm'

Третья строка, строго говоря, лишняя, потому что члены локальной группы администраторов и так заходят по RDP. Но вреда она не приносит и делает намерение явным на случай, если позже вы уберёте эту учётную запись из группы администраторов.

В-третьих: не закрывайте RDP-сеанс, в котором вы работаете, на всё время перенастройки и проверяйте каждое изменение вторым, новым подключением. Существующий сеанс новые правила файрвола сразу не разрывают, а новое подключение сорвётся немедленно. Так вы заметите ошибку, пока ещё можете её отменить.

Принудительная проверка подлинности на уровне сети

Без проверки подлинности на уровне сети (по-английски Network Level Authentication, сокращённо NLA) сервер сначала создаёт сеанс, рисует экран входа и только потом спрашивает учётные данные. Каждая анонимная попытка подключения стоит оперативной памяти и времени CPU, а весь код за экраном входа доступен ещё до какой-либо аутентификации. Именно там сидели тяжёлые уязвимости RDP прошлых лет.

С NLA сервер проверяет учётные данные через CredSSP до того, как сеанс вообще возникнет. Бот без действительных данных получает только отклонённое TLS-соединение.

Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' -Value 1
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'SecurityLayer' -Value 2

SecurityLayer со значением 2 принуждает к TLS при согласовании. Значение 1 (согласование) позволяет клиенту в крайнем случае откатиться на старый уровень безопасности RDP, 0 это старый уровень без TLS. Проверка:

Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' | Select-Object UserAuthentication, SecurityLayer

Перезагрузка не нужна, значения действуют для каждого вновь устанавливаемого соединения. Существующие сеансы продолжают работать без изменений, что удобно, если вы как раз отрезали себе клиент.

Если после этого никто больше не заходит

После включения NLA регулярно всплывают два типа ошибок.

«Удалённому компьютеру требуется проверка подлинности на уровне сети, которую данный компьютер не поддерживает. Обратитесь за помощью к системному администратору или в службу технической поддержки.» Клиент слишком стар или не понимает CredSSP. Актуальные клиенты Windows умеют это уже много лет. В Linux FreeRDP требует ключ /sec:nla, Remmina требует выставить протокол безопасности на NLA, а очень старые клиенты macOS не подключатся в принципе. Решение — всегда более новый клиент, а не отключение NLA.

Вход не проходит с правильным паролем. Это ловушка, о которой умалчивает большинство руководств: если у учётной записи установлен флаг «Требовать смену пароля при следующем входе в систему» или срок действия пароля истёк, с включённым NLA эта учётная запись вообще не сможет войти. CredSSP не умеет менять пароль, так и задумано. Сервер в зависимости от версии сообщает об ошибке аутентификации или просто о неверных учётных данных. Выход ведёт через консоль в личном кабинете, либо вы исключаете сервисные учётные записи из срока действия пароля:

Set-LocalUser -Name 'kh-adm' -PasswordNeverExpires $true

Блокировка учётной записи: самый действенный отдельный переключатель

Сильный пароль защищает от угадывания, блокировка учётной записи защищает от неограниченного угадывания. Без неё атакующий вправе делать миллионы попыток в неделю, с ней их десять за четверть часа. Задайте порог, длительность блокировки и окно наблюдения одним вызовом, иначе Windows отклонит длительность, пока порог ещё равен 0:

net accounts /lockoutthreshold:10 /lockoutduration:15 /lockoutwindow:15

Длительность блокировки всегда должна быть больше окна наблюдения или равна ему. Проверка результата:

net accounts

Более новые версии Windows уже приносят значение такого порядка по умолчанию, старые установки и многие образы провайдеров нет. Проверка ничего не стоит.

Включить в блокировку встроенную учётную запись администратора

Исторически именно та учётная запись, которую атакующий пробует первой, была исключена из блокировки. Для этого есть политика «Разрешить блокировку учётной записи администратора». Правда, она требует более свежей версии, а именно Windows 11 22H2 или Windows Server 2025. На Windows Server 2022 (сборка 20348) экспорт политики безопасности вообще не содержит строки AllowAdministratorLockout, в разделе [System Access] там стоят только значения вроде LockoutBadCount и MinimumPasswordLength. Поэтому сначала проверьте, что именно экспортирует ваша система:

secedit /export /cfg C:\secpol.inf
Select-String -Path C:\secpol.inf -Pattern 'AllowAdministratorLockout'

Если совпадений нет, ваша сборка эту политику не знает, и раздел для вас на этом закрыт. Ровно здесь кроется ловушка, которую создаёт большинство руководств: обычный однострочник с -replace заменяет строку, которой в файле вообще нет, но об ошибке не сообщает, а последующий secedit /configure рапортует об успехе. После этого вы считаете, что блокировка администратора включена, хотя не изменилось ничего.

На сборке, которая эту политику знает, вы меняете строку, если она есть, а иначе вставляете её. Такой вариант покрывает оба случая:

$c = Get-Content C:\secpol.inf
if ($c -notmatch 'AllowAdministratorLockout') { $c = $c -replace '(LockoutBadCount = \d+)', "`$1`r`nAllowAdministratorLockout = 1" } else { $c = $c -replace 'AllowAdministratorLockout = 0','AllowAdministratorLockout = 1' }
$c | Set-Content C:\secpol.inf -Encoding Unicode

-Encoding Unicode обязателен, и это не деталь: файл INF должен быть в UTF-16 LE с меткой порядка байтов. Иначе Set-Content в Windows PowerShell 5.1 запишет ANSI, и secedit откажется принимать файл. Перед обратной записью файл проверяет secedit /validate C:\secpol.inf, затем:

secedit /configure /db C:\Windows\security\local.sdb /cfg C:\secpol.inf /areas SECURITYPOLICY

И после этого обязательно перечитайте результат, потому что успешный запуск secedit в этом месте ничего не доказывает. Экспортируйте политику ещё раз во второй файл и посмотрите, действительно ли значение стоит с 1.

На контроллере домена эти локальные настройки не работают. Там политика блокировки задаётся в Default Domain Policy: Конфигурация компьютера, Конфигурация Windows, Параметры безопасности, Политики учётных записей.

Обратная сторона и как вернуться обратно

Блокировка учётной записи работает и против вас: тот, кто знает ваше имя пользователя, может держать его заблокированным постоянно, отправляя каждые 15 минут по десять неверных паролей. Именно поэтому блокировка остаётся лишь второй линией обороны, а первой служит ограничение по IP-адресам из следующего раздела.

Если локальная учётная запись заблокирована, клиент сообщает примерно следующее: «Учётная запись заблокирована и не может быть использована для входа в систему». Простейший путь назад — подождать, потому что по истечении длительности блокировки Windows снимает её сам. Кто ждать не хочет, снимает блокировку через консоль:

$u = [ADSI]"WinNT://./kh-adm,user"; $u.IsAccountLocked = $false; $u.SetInfo()

В Active Directory это короче: Unlock-ADAccount -Identity kh-adm.

Пароли, которые придают блокировке смысл

Десять попыток за четверть часа становятся препятствием только тогда, когда пароль не стоит на третьем месте в любом списке. Минимальную длину и сложность задают так:

net accounts /minpwlen:14

Правило сложности снова лежит в политике безопасности, раздел [System Access], ключ PasswordComplexity = 1. Путь тот же, что и выше, через secedit.

Два замечания из практики. Во-первых, принудительная смена пароля каждые 30 дней доказуемо даёт мало и в сочетании с NLA порождает ровно ту проблему входа из предыдущего раздела. Длинные пароли, заданные один раз и сохранённые в менеджере паролей, лучше. Во-вторых, политика действует только для вновь задаваемых паролей. Существующий пароль из шести символов остаётся действительным, пока вы его не смените.

Ограничить доступ известными IP-адресами

Это та мера, которая действительно прекращает атакующий трафик, причём полностью. Все правила группы удалённого рабочего стола можно ограничить списком адресов. Используйте языконезависимый идентификатор группы, чтобы скрипт работал и на английских установках:

Get-NetFirewallRule -Group '@FirewallAPI.dll,-28752' | Select-Object Name, DisplayName, Enabled, Profile
Set-NetFirewallRule -Group '@FirewallAPI.dll,-28752' -RemoteAddress @('203.0.113.10','198.51.100.0/24')

Ожидайте при этом больше попаданий, чем правил, которые вы предполагаете. -Group затрагивает все правила группы, на тестовой системе это оказалось шесть штук, включая теневые правила и дополнительные копии с именами-GUID в профиле Public. Так и задумано, и это правильно, иначе одна из копий осталась бы открытой. Предшествующий вывод списка через Get-NetFirewallRule заранее показывает, что будет затронуто.

Проверка, какие адреса действительно сохранились:

Get-NetFirewallRule -Group '@FirewallAPI.dll,-28752' | Get-NetFirewallAddressFilter

Если вы сменили порт, встроенные правила больше не срабатывают, потому что они жёстко привязаны к 3389. Тогда нужно собственное правило:

New-NetFirewallRule -DisplayName 'RDP ограничен' -Direction Inbound -Protocol TCP -LocalPort 34567 -RemoteAddress '203.0.113.10' -Action Allow -Profile Any

Страховка от собственного правила файрвола

Кто ошибётся в своём IP-адресе или впишет динамический адрес, тот надёжно отрежет себе доступ. Поэтому заранее создайте задание, которое через десять минут само снимет ограничение:

Set-Content -Path C:\rdp-rettung.ps1 -Value "Set-NetFirewallRule -Group '@FirewallAPI.dll,-28752' -RemoteAddress Any"
Register-ScheduledTask -TaskName 'RDP-восстановление' -Action (New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-ExecutionPolicy Bypass -File C:\rdp-rettung.ps1') -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(10)) -User 'SYSTEM' -RunLevel Highest

Если новое подключение работает, задание удаляется снова:

Unregister-ScheduledTask -TaskName 'RDP-восстановление' -Confirm:$false

Если фиксированного IP-адреса у вас нет, чистое решение — не выставлять RDP в интернет вообще, а подключаться через VPN. Как это настроить, описано в нашем руководстве по серверу WireGuard VPN. Тогда сервер слушает только на VPN-адресе, а порт 3389 полностью исчезает из интернета.

Смена порта и что она реально даёт

Другой порт — не мера безопасности, а мера защиты от шума. Основная масса ботов сканирует исключительно 3389 и после смены вас уже не находит, что, как правило, резко снижает ваше число событий 4625 и снова делает журналы читаемыми. Кто ищет целенаправленно, тот найдёт службу всё равно: поисковые системы по открытым службам распознают RDP по отпечатку протокола независимо от порта, а полное сканирование 65535 портов занимает секунды.

Смена порта, таким образом, осмысленна как дополнение, но никогда как замена NLA, блокировки учётных записей и ограничения по адресам. Практическую реализацию, включая ту часть, которую почти все руководства делают неправильно, а именно без перезагрузки, мы описали отдельно: Смена порта RDP без перезагрузки. В любом случае не забудьте создать правило файрвола для нового порта, прежде чем переключать службу.

Разбор журналов входа

Сначала журналирование вообще должно быть включено. Названия подкатегорий в auditpol локализованы, английское название на русской системе завершается ошибкой «Произошла ошибка 0x00000057: Неверный параметр.» GUID же работает в любой языковой версии:

auditpol /set '/subcategory:{0CCE9215-69AE-11D9-BED3-505054503030}' /success:enable /failure:enable
auditpol /get '/subcategory:{0CCE9215-69AE-11D9-BED3-505054503030}'

Одинарные кавычки вокруг всего параметра — не косметика, а обязательное условие. Иначе PowerShell трактует фигурные скобки как блок скрипта и удаляет их, из одного аргумента получаются три, и auditpol прерывается с Произошла ошибка 0x00000057: Неверный параметр. и кодом выхода 87, а следом выводит текст справки. В cmd.exe запись без кавычек работает, в PowerShell нет. С кавычками оба вызова проходят с кодом выхода 0, а контрольный запрос отвечает Вход в систему Успех и отказ.

Под обстрелом журнал безопасности переполняется за считаные часы и перезаписывает ровно те записи, которые вам нужны. Дайте ему больше места:

wevtutil sl Security /ms:1073741824

Затем самые частые адреса источников за последнюю неделю, отсортированные по количеству. Вариант через структуру XML надёжнее, потому что он не зависит от порядка полей:

Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-7)} -ErrorAction SilentlyContinue | ForEach-Object { ([xml]$_.ToXml()).Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' } | Select-Object -ExpandProperty '#text' } | Group-Object | Sort-Object Count -Descending | Select-Object -First 15 Count, Name

-ErrorAction SilentlyContinue нужен в каждом таком вызове. Get-WinEvent обрывается красной ошибкой, как только за указанный период нет ни одного подходящего события: «Не найдено событий, соответствующих указанным условиям выбора.» На только что защищённом сервере это как раз нормальный случай, то есть команда падает именно при том успехе, к которому ведёт это руководство.

Решающий вопрос, однако, не в том, кто пытался, а в том, удалось ли кому-то. Успешные входы по удалённому рабочему столу несут идентификатор события 4624 с типом входа 10 (RemoteInteractive):

Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624; StartTime=(Get-Date).AddDays(-7)} -ErrorAction SilentlyContinue | Where-Object { $_.Properties[8].Value -eq 10 } | Select-Object TimeCreated, @{n='Пользователь';e={$_.Properties[5].Value}}, @{n='Источник';e={$_.Properties[18].Value}} | Format-Table -AutoSize

Если там стоит имя пользователя или адрес источника, который вы не можете сопоставить ни с чем своим, значит кто-то нашёл действующий пароль. Тогда доводка политик уже не поможет, тогда сервер нужно устанавливать заново.

Дополнительно стоит заглянуть в собственный журнал RDP. Событие 1149 называет пользователя, домен и адрес источника каждого авторизованного подключения одной строкой:

Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' -FilterXPath '*[System[EventID=1149]]' -MaxEvents 25 | Format-List TimeCreated, Message

Переименовать администратора, а лучше отключить

Встроенная учётная запись администратора — единственное имя пользователя, которое наверняка знает каждый атакующий. Переименование ничего не стоит:

Rename-LocalUser -Name 'Administrator' -NewName 'kh-svc'

Но отдавайте себе отчёт в границах этой меры: SID учётной записи по-прежнему заканчивается на -500, и любой аутентифицированный доступ может через него получить новое имя. Против массовых ботов переименование работает, против целенаправленного атакующего, который уже поставил ногу в дверь, нет. Вот как найти учётную запись, несмотря на новое имя:

Get-LocalUser | Where-Object { $_.SID.Value -like '*-500' } | Select-Object Name, Enabled

Заметно действеннее полностью отключить встроенную учётную запись после того, как ваша собственная учётная запись администратора из раздела «Обеспечьте себе путь назад» доказанно работает. Проверьте вход под новой учётной записью во втором сеансе, и только тогда:

Disable-LocalUser -Name 'kh-svc'

Дополнительно ограничьте доступ по RDP теми людьми, которым он нужен. По умолчанию по RDP заходит любой член локальной группы администраторов, в том числе служебные учётные записи, которым этого делать не следует. Кто имеет право сейчас, покажет:

Get-LocalGroupMember -SID 'S-1-5-32-555'

По чему видно, что всё действительно сработало

Отмечать галочки, а не надеяться. Эти пять проверок скажут вам, дошла ли перенастройка:

  1. NLA: Get-ItemProperty для RDP-Tcp выдаёт UserAuthentication : 1 и SecurityLayer : 2. Новое подключение теперь спрашивает учётные данные до установления соединения, а не уже на экране входа внутри окна.
  2. Блокировка: net accounts показывает порог блокировки, отличный от «Никогда». Контрольная проверка одноразовой учётной записью: после одиннадцатого неверного пароля клиент должен выдать сообщение о блокировке, а не сообщение о неверных учётных данных.
  3. Файрвол: Get-NetFirewallAddressFilter показывает ваши адреса вместо Any. Проверка подключения с чужого адреса должна уходить в ошибку по тайм-ауту, а не в приглашение ко входу. Проверьте это снаружи командой Test-NetConnection -ComputerName ваш-сервер -Port 3389, результат должен быть TcpTestSucceeded : False.
  4. Журналы: auditpol /get сообщает для подкатегории успех и отказ.
  5. Число: посчитайте события 4625 ещё раз через 24 часа после перенастройки. Оно должно быть заметно ниже. Если оно остаётся высоким, одно из ваших правил не действует, чаще всего потому, что существует второе, более открытое правило файрвола для порта 3389, созданное образом провайдера или установкой какой-то программы. Эта строка его найдёт: Get-NetFirewallPortFilter | Where-Object { $_.LocalPort -eq 3389 } | Get-NetFirewallRule | Select-Object DisplayName, Enabled, Profile, Action. Порядок в конвейере выбран намеренно. Если его развернуть и сначала прогнать все правила файрвола через Get-NetFirewallPortFilter, вызов по замерам занимает около 12 секунд вместо одной, а главное, в выводе не будет имени правила: вы увидите четыре попадания, не узнав, что это за правила.

Если вы всё равно как раз настраиваете сервер с нуля, пройдите эти пункты сразу в начале, а не дооснащайте их потом. Для систем Linux действует та же схема, только с SSH вместо RDP, она описана в статье Защита SSH и вход по ключу, а порядок первых шагов вы найдёте в нашем чек-листе для новых root-серверов.

Чего защита RDP не покрывает

Меры выше защищают от попыток входа. Они не защищают от объёмных атак, цель которых сделать сервер недоступным через сетевое подключение. Против них помогает только фильтрация в сети перед сервером. Как работают такие атаки, описано в статье Что такое DDoS-атака, а какие меры остаются осмысленными на стороне сервера, в статье Защита сервера от DDoS-атак. Все серверы KernelHost стоят в дата-центре maincubes во Франкфурте-на-Майне за фильтрацией, которая перехватывает атакующий трафик ещё до сервера.

И они не заменяют резервное копирование. Сервер, на котором кто-то успешно вошёл, больше не заслуживает доверия, как бы быстро вы ни сменили пароль после этого. Единственный надёжный путь назад — резервная копия, сделанная до инцидента.

Частые вопросы

Достаточно ли сменить порт RDP с 3389 на другой?
Нет. Другой порт отсекает массу ботов, которые сканируют исключительно 3389, и заметно снижает число попыток атак в журналах. Но целенаправленные сканеры распознают RDP по отпечатку протокола на любом порту. Смена порта — разумное дополнение к NLA, блокировке учётных записей и ограничению по IP, но не замена им.
Я сам себе закрыл доступ после установки правила файрвола. Что теперь?
Подключитесь через VNC-консоль в личном кабинете, она работает независимо от RDP и сети, и снимите ограничение командой Set-NetFirewallRule -Group '@FirewallAPI.dll,-28752' -RemoteAddress Any. Чтобы вообще не попадать в такую ситуацию, до перенастройки создайте запланированное задание, которое выполнит именно эту команду автоматически через десять минут.
Почему пользователь не может войти с правильным паролем с тех пор, как включён NLA?
Вероятно, у учётной записи истёк срок действия пароля или установлен флаг «Требовать смену пароля при следующем входе в систему». CredSSP, протокол за NLA, не умеет проводить смену пароля, поэтому вход срывается полностью. Смените пароль через консоль или исключите сервисные учётные записи из срока действия с помощью Set-LocalUser -PasswordNeverExpires $true.
Распространяется ли блокировка учётных записей на встроенную учётную запись администратора?
Только если активна политика «Разрешить блокировку учётной записи администратора», а она требует Windows 11 22H2 или Windows Server 2025. На Windows Server 2022 (сборка 20348) экспорт через secedit /export вообще не содержит ключа AllowAdministratorLockout: поиск с заменой уходит там в пустоту, но об ошибке не сообщает, а secedit /configure всё равно рапортует об успехе. Поэтому проверьте через Select-String в экспорте, знает ли ваша сборка эту политику вообще. Задаётся она не через net accounts, а через secedit строкой AllowAdministratorLockout = 1 в разделе [System Access]. Блокировка при этом действует только на сетевые входы вроде RDP, вход с локальной консоли остаётся возможным.
Может ли атакующий через блокировку учётных записей закрыть мне доступ навсегда?
Да, это обратная сторона. Тот, кто знает имя пользователя, может держать учётную запись заблокированной целенаправленными неудачными попытками. Именно поэтому первой линией обороны служит ограничение по известным IP-адресам или доступ через VPN, а блокировка учётной записи только второй. Вторая учётная запись администратора с другим именем тоже полезна.
По чему понять, что атака удалась?
Найдите в журнале безопасности события с идентификатором 4624 и типом входа 10 и сравните имя пользователя и адрес источника со своими подключениями. Если что-то не сходится, вход был чужим. Дополнительно стоит посмотреть журнал Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational, событие 1149. При совпадении доводка настроек уже не поможет, сервер нужно переустановить из резервной копии, сделанной до инцидента.

RDP Windows Server Безопасность PowerShell Удалённый рабочий стол Файрвол Брутфорс Усиление защиты