wGrow
menu
Field note · Migrated 2026-04-08 · 5 min

Watching RDP for brute-force, in 2026: when the homemade service still wins.

Our 2023 RDP-monitoring Windows Service is still in production at three clients. Here's the 2026 reality: most teams should turn off RDP entirely, but for the ones that can't, the design holds.

In 2023 we shipped a small Windows Service (RDPMonitorService) that watched the Security Event Log for failed RDP authentications, fired email alerts to the admin, and temporarily blocked offending IPs. We wrote it up as Team-Notes #82.

Three years on. The service is still in production at three client sites. RDP itself has aged worse than the service did. Here is the rewrite.

What the original got right

  • Watching Event ID 4625 in the Security log is still the correct signal.
  • A 3-attempts-in-5-minutes / 24-hour-block default was conservative and saved several clients from sustained brute-force noise.
  • Running the service under a dedicated, least-privileged account, not LocalSystem.
  • DPAPI for credential handling.

What we’d say first in 2026

Turn RDP off if you can. Use a bastion + SSO + Just-in-Time access instead.

We mean this. Most workloads that had RDP exposed (even via NLA, even on a non-default port) can be served by an Entra ID / Identity Center fronted bastion with session recording. The number of “RDP got brute-forced” incidents in our own client book over 2024–2025 is zero on bastion-only estates and non-zero on RDP-exposed estates.

When the homemade service is still right

Three real cases:

  1. Industrial / lab environments that have a Windows host on a tightly-firewalled VLAN, a single allow-listed admin IP, and operators who must use RDP from a specific physical workstation. Bastion is overkill; a homemade monitor is right-sized.
  2. Air-gapped or near-air-gapped estates where you cannot bring in a bastion product, but you can run a small in-house service with explicit code review.
  3. Cost-sensitive small clients for whom the licensing of a commercial bastion or PAM product is the actual blocker.

For all three, the 2023 design works. Below is what we’d update.

Updates we’d make to the 2023 design

1. PowerShell over C#, in some cases

The 2023 article showed C# with EventLogQuery / EventLogReader. For the simpler deployments above (a single Windows host, no central log pipeline), a PowerShell script run as a scheduled task is now usually a better choice than a full Windows Service:

# Watch Security log, block IPs after 3 fails in 5 min for 24 hours.
Get-WinEvent -LogName Security -FilterXPath @'
*[System[Provider[@Name='Microsoft-Windows-Security-Auditing']
  and (EventID=4625) and TimeCreated[timediff(@SystemTime) <= 300000]]]
'@ |
  Group-Object { $_.Properties[19].Value } |    # source IP
  Where-Object { $_.Count -ge 3 } |
  ForEach-Object {
    $ip = $_.Name
    if ($ip -and $ip -ne '-') {
      New-NetFirewallRule -DisplayName "RDPBlock-$ip" `
        -Direction Inbound -RemoteAddress $ip -Action Block `
        -Description "auto-block $(Get-Date -Format o)" | Out-Null
      Write-EventLog -LogName Application -Source "RDPMonitor" `
        -EntryType Warning -EventId 1001 -Message "Blocked $ip"
    }
  }

A 30-line scheduled task replaces a few hundred lines of C#, with no installer.

2. Push events off-box

The 2023 service emitted to the local Application log. In 2026, ship the events to a centralised pipeline (Sentinel, OpenSearch, or a simple syslog endpoint). A monitor whose alerts only land on the box being attacked is a brittle monitor.

3. Don’t trust unblock-by-timer alone

The original design auto-unblocked an IP after 24 hours. We’ve kept that in the simple deployments and dropped it in the more sensitive ones, where an admin reviews the block list weekly. Auto-unblocking a real attacker IP after 24 hours is a slightly bad default for high-value workloads.

4. MFA is still the thing

If RDP must be exposed, the only durable mitigation is MFA on the RDP login itself — Duo, Yubikey + Smart-Card, or Windows Hello for Business in a domain context. The brute-force monitor is a defence in depth layer. Without MFA, a sufficiently patient attacker wins.

The agent-era footnote

Could an LLM-driven agent run as the RDP-monitor brain, reading the event log and deciding when to block? Technically yes. We have not seen a case where it adds value over a deterministic rule. The decision space (block, unblock, escalate) is small; deterministic logic is testable; LLM cost and latency are unjustified. This is one of the cases where the agent does not belong.

What we cut from the original

  • The framing of the homemade service as the default path. It isn’t. The default path in 2026 is “turn RDP off.”
  • The implication that the 24-hour auto-unblock is universally safe. It isn’t.

What carries over unchanged

The pattern: read the event log, count failures by IP, write a firewall rule, log to a place a human reads. Boring. Effective. Compounds.

— wGrow studio · migrated from Team-Notes #82