WindowsMay 28, 2026 7 views

Block RDP brute-force attacks on a Windows VPS in 2026

Block RDP brute-force attacks on a Windows VPS in 2026

You manage a Windows VPS exposed on the Internet and discover in the Event Viewer thousands of failed RDP login attempts per day. This is normal: any Windows VPS with port 3389 open is constantly scanned by botnets. Without protection, a single misconfiguration is enough for an attacker to gain access.

This guide explains how to block RDP brute-force attacks at 3 levels: (1) basic RDP hygiene, (2) automatic banning of attacking IPs, (3) VPN/Gateway architecture to eliminate exposure. You will leave with a complete tested solution that is free and adaptable to any Windows Server 2019, 2022, or 2025 VPS.

Quick Summary - The Vital Minimum in 5 Minutes

  • Enable NLA (Network Level Authentication): System → Remote Desktop → Advanced → require NLA
  • Account lockout policy: 5 failures = block for 30 min (via secpol.msc)
  • Install IPBan (1 PowerShell command, auto ban of malicious IPs):
iex (irm https://raw.githubusercontent.com/DigitalRuby/IPBan/master/IPBanCore/Windows/Scripts/install-latest.ps1)
  • Change RDP port 3389 → 49xxx via regedit (slows down automated scans).

Details and advanced options below.

Why RDP is the Number 1 Target for Botnets

The Remote Desktop Protocol is exposed on port 3389/TCP, and this port is continuously scanned across the entire IPv4 by automated botnets. A new Windows VPS brought online today without any other action will typically receive:

  • 5,000 to 50,000 login attempts per day within the first few hours.
  • Attempts on default accounts: Administrator, admin, user, test, sql, backup.
  • Attempts on standard Windows accounts discovered via SMB / NetBIOS enumeration.
  • Attempts with password lists (rockyou.txt and derivatives), at a rate of 1 attempt every 1 to 5 seconds per IP.

If the attacker finds a weak password, they:

  • Create a new "discreet" administrator account (e.g., Helpdesk$, IUSR_X).
  • Disable Windows Defender.
  • Install a cryptocurrency miner, a spam bot, or ransomware.
  • Sell access on black markets (e.g., historical xDedic).

The average time between compromise and first malicious impact: 15 minutes.

Hence the goal of this guide: to make your VPS uninteresting for bots and block the IPs that still try.

Diagnosing an Ongoing Brute-Force Attack

Counting Failed Attempts

Open PowerShell as an administrator and run:

# Number of login failures in the last 24 hours
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625; StartTime=(Get-Date).AddDays(-1)} |
    Measure-Object | Select-Object -ExpandProperty Count

Event ID 4625 is the Windows identifier for a failed authentication. If you see more than a few dozen per day, you are being targeted.

Viewing Attacking IPs Clearly

Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625} -MaxEvents 1000 |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' } | Select-Object -ExpandProperty '#text'
    } |
    Where-Object { $_ -and $_ -ne '-' } |
    Group-Object | Sort-Object Count -Descending | Select-Object -First 20

You will get the top 20 attacking IPs with their number of attempts. If an IP accumulates more than 50 attempts, it is clearly malicious - it needs to be banned.

Identifying Targeted Accounts

Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625} -MaxEvents 1000 |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' } | Select-Object -ExpandProperty '#text'
    } |
    Group-Object | Sort-Object Count -Descending | Select-Object -First 20

If you see Administrator, admin, or non-existent accounts, it is a blind brute-force attack (the most common case).

Layer 1 - RDP Hygiene (the Non-Negotiable Minimum)

Before discussing anti-brute-force tools, configure these 5 basic settings. No tool will compensate for their absence.

1.1 Enable NLA (Network Level Authentication)

NLA forces authentication BEFORE the RDP session opens. Without NLA, the attacker can launch the session, see the login screen, and brute-force much faster.

# Enable NLA via registry
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
    -Name "UserAuthentication" -Value 1

Or via interface: sysdm.cpl → Remote tab → check Only allow connections with NLA.

1.2 Rename (or Disable) the Administrator Account

The Administrator account represents 80% of brute-force targets. Rename it:

$adminSID = "S-1-5-21-*-500"
$adminAccount = Get-LocalUser | Where-Object { $_.SID -like $adminSID }
Rename-LocalUser -Name $adminAccount.Name -NewName "Admin_$(Get-Random -Maximum 9999)"

Better yet: create a new admin account with an unguessable name and completely disable Administrator:

Disable-LocalUser -Name "Administrator"

⚠️ Before disabling Administrator, ensure that another admin account is working and that you can connect to it via RDP. Otherwise, you will lock yourself out of the VPS.

1.3 Account Lockout Policy

5 failed attempts = account locked for 30 minutes. Configuration via secpol.msc → Account Policies → Account Lockout Policy:

Parameter Recommended Value
Account lockout threshold 5 attempts
Account lockout duration 30 minutes
Reset account lockout counter after 30 minutes

Or via command line:

net accounts /lockoutthreshold:5 /lockoutduration:30 /lockoutwindow:30

1.4 Strong Password Policy

net accounts /minpwlen:14 /maxpwage:90 /minpwage:1 /uniquepw:5
  • Minimum 14 characters
  • Expiration every 90 days
  • 1 day minimum before change (anti-cycling)
  • Last 5 passwords prohibited

1.5 Change the Default RDP Port (3389 → Random Port)

This does not block a targeted attacker, but eliminates 95% of the noise from automated scans.

$newPort = 53389  # Choose a random port between 49152 and 65535
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
    -Name "PortNumber" -Value $newPort

# Open the new port in the firewall
New-NetFirewallRule -DisplayName "RDP Custom Port" -Direction Inbound `
    -LocalPort $newPort -Protocol TCP -Action Allow

# Close port 3389
Get-NetFirewallRule -DisplayName "Remote Desktop*" | Disable-NetFirewallRule

To reconnect, now use <ip>:53389 in the RDP client. Restart the server for the new port to take effect.

⚠️ Before restarting, test the new firewall rule from another IP or note the out-of-band console (KVM/IPMI console from your host) to regain access in case of error.

1.6 Limit RDP Access by IP (Firewall Scope Rules)

If you always connect from the same IPs (office, VPN, fixed IP), only allow those IPs:

# Replace with your allowed IPs (comma-separated)
$allowedIPs = "203.0.113.42", "198.51.100.10"

Get-NetFirewallRule -DisplayName "*Remote Desktop*" | ForEach-Object {
    Set-NetFirewallRule -Name $_.Name -RemoteAddress $allowedIPs
}

This is the most effective measure: if the attacker does not come from a whitelisted IP, their packet is rejected before even reaching the RDP service. Brute-force impossible.

For mobile use, see layer 3 with VPN.

Layer 2 - Automatic Banning of Malicious IPs

Your basic settings are in place, but bots will continue to try. Auto-ban automatically detects IPs that multiply failures and bans them via Windows Firewall.

Comparison of the 6 Main Tools

Tool License Maintenance Type Recommendation
IPBan (DigitalRuby) Free (Pro paid) Active 2025+ Windows Service ⭐ The most popular and robust
Fail2Ban4Win (Aldaviva) Free, MIT Active Windows Service Excellent, modern, .NET
EZWinBan (neil-sabol) Free Moderate PS Script + Task Scheduler Simple, ideal for small server
wail2ban (glasnt) Free Low (2017) PS Script Historical, to avoid
EvlWatcher Free Active Windows Service Good, less documented
RDPGuard Commercial (~$99) Active Windows Service Also covers FTP/SMTP/SQL

Recommendation for OuiHeberg: The free version of IPBan covers 99% of needs. If you want a 100% modern open-source alternative, use Fail2Ban4Win.

IPBan Tutorial - Installation, Configuration, Verification

Step 1 - Installation in One Command

Open PowerShell as an administrator and run:

iex (irm https://raw.githubusercontent.com/DigitalRuby/IPBan/master/IPBanCore/Windows/Scripts/install-latest.ps1)

The script downloads the latest version, installs it as a Windows service, and configures the IPBan service to start automatically.

Step 2 - Check that the Service is Running

Get-Service -Name IPBan

Should display Status: Running.

Step 3 - Edit the Configuration

The configuration file is located at:

C:\Program Files\IPBan\ipban.config

Essential parameters:

<!-- Number of failures before ban -->
<add key="FailedLoginAttemptsBeforeBan" value="5"/>

<!-- Duration of the ban (in days.hours:minutes:seconds) -->
<!-- Special format: 00:30:00 = 30 minutes -->
<add key="ExpireTime" value="01:00:00:00"/>  <!-- 1 day -->

<!-- Failure counting window -->
<add key="CycleTime" value="00:00:00:15"/>  <!-- 15 seconds -->

<!-- Whitelist (IPs never banned) -->
<add key="Whitelist" value="203.0.113.42,198.51.100.0/24"/>

<!-- Permanent blacklist -->
<add key="Blacklist" value=""/>

To enable a strict policy (recommended for production):

  • FailedLoginAttemptsBeforeBan = 3
  • ExpireTime = 30:00:00:00 (30 days)
  • Whitelist all your management IPs.

Step 4 - Restart the Service After Modification

Restart-Service IPBan

Step 5 - View Banned IPs in Real Time

IPBan automatically creates a firewall rule named IPBan_*. To view it:

Get-NetFirewallRule -DisplayName "IPBan*" |
    Get-NetFirewallAddressFilter |
    Select-Object -ExpandProperty RemoteAddress

Or more visually, by looking at the IPBan logs:

Get-Content "C:\Program Files\IPBan\ipban.log" -Tail 50 -Wait

You will see live each banned IP with the timestamp and number of failures.

Step 6 - Unban an IP by Mistake

# Method 1: remove from the firewall directly
Remove-NetFirewallRule -DisplayName "IPBan_*" -ErrorAction SilentlyContinue

# Method 2: add the IP to the whitelist in ipban.config then restart
Restart-Service IPBan

Recommended Configuration by Profile

Profile Attempts Before Ban Ban Duration Cycle
Personal / dev server 5 1 day 15 sec
Exposed production server 3 7 days 15 sec
Critical server 2 30 days 15 sec
Bastion / single server 1 365 days 15 sec

Alternative - Custom PowerShell Auto-Ban Script (No Dependencies)

If you want no external dependencies or third-party services, here is a standalone PowerShell script that does the job. It scans Event ID 4625 and bans any IP accumulating more than N failures over 1 hour.

Save this script in C:\Scripts\AutoBanRDP.ps1:

# === OuiHeberg - Auto-ban RDP brute-force (no dependencies) ===

param(
    [int]$Threshold = 10,       # Number of failures before ban
    [int]$WindowHours = 1,      # Counting window in hours
    [int]$BanDays = 7           # Duration of the ban in days
)

$ruleName = "OuiHeberg_AutoBan_RDP"
$startTime = (Get-Date).AddHours(-$WindowHours)

# Retrieve attacking IPs
$attackers = Get-WinEvent -FilterHashtable @{
    LogName   = 'Security'
    ID        = 4625
    StartTime = $startTime
} -ErrorAction SilentlyContinue | ForEach-Object {
    $xml = [xml]$_.ToXml()
    $ip = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text'
    if ($ip -and $ip -ne '-' -and $ip -ne '::1' -and $ip -ne '127.0.0.1') { $ip }
} | Group-Object | Where-Object { $_.Count -ge $Threshold } | Select-Object -ExpandProperty Name

if (-not $attackers) {
    Write-Host "No IP to ban (threshold = $Threshold over $WindowHours h)." -ForegroundColor Green
    return
}

# Retrieve existing rule or create it
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($existingRule) {
    $currentIPs = ($existingRule | Get-NetFirewallAddressFilter).RemoteAddress
    $allIPs = ($currentIPs + $attackers) | Sort-Object -Unique
    Set-NetFirewallRule -DisplayName $ruleName -RemoteAddress $allIPs
} else {
    New-NetFirewallRule -DisplayName $ruleName `
        -Direction Inbound -Action Block `
        -RemoteAddress $attackers `
        -Description "Auto-ban OuiHeberg: created on $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
}

Write-Host "$($attackers.Count) IP banned: $($attackers -join ', ')" -ForegroundColor Yellow

# Schedule automatic unban (remove rule after BanDays days)
$logPath = "C:\Scripts\AutoBanRDP.log"
$attackers | ForEach-Object {
    "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`tBAN`t$_`tEXPIRE=$((Get-Date).AddDays($BanDays))" | Out-File -Append $logPath
}

Schedule Execution Every 15 Minutes

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\AutoBanRDP.ps1"

$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
    -RepetitionInterval (New-TimeSpan -Minutes 15) `
    -RepetitionDuration (New-TimeSpan -Days 365)

Register-ScheduledTask -TaskName "OuiHeberg-AutoBanRDP" `
    -Action $action -Trigger $trigger `
    -User "SYSTEM" -RunLevel Highest -Force

The script runs every 15 minutes in the background. You can adjust $Threshold, $WindowHours, and $BanDays according to your tolerance.

Advantages of the Custom Script

  • ✓ No external dependencies.
  • ✓ 100% auditable and modifiable code.
  • ✓ Complete local logs.

Limitations

  • ❌ No automatic unban (to be scripted additionally).
  • ❌ No multi-service detection (only RDP via Event 4625).
  • ❌ For > 10,000 banned IPs, the firewall rule becomes heavy - prefer IPBan.

Layer 3 - Secure Architecture (the Real Solution)

Layers 1 and 2 drastically reduce risks. Real security means not exposing RDP at all. Three recommended architectures:

Option A - VPN in Front of RDP (the Simplest)

  • Install a WireGuard or OpenVPN server on your VPS (or in front).
  • Block the RDP port except from the internal IP of the VPN.
  • You connect first via VPN, then via RDP as if you were on the LAN.

Advantages: RDP becomes invisible from the Internet. Cost: 30 min installation, native performance.

Option B - RD Gateway (Remote Desktop Gateway)

Native Microsoft solution: an RD Gateway server acts as a HTTPS proxy for your RDP sessions. The client connects via HTTPS (port 443) to the Gateway, which relays internally in RDP.

Advantages: compatible with standard RDP client, HTTPS encryption, AD integration. Disadvantage: requires an RDS license and more configuration.

Option C - Bastion / Jump Host

A minimalist "bastion" server with only SSH or RDP via key. All your connections pass through it. If the bastion is compromised, your real servers remain untouched.

Recommended for infrastructures with more than 3-5 VPS.

Verification and Monitoring

Measure Effectiveness After 24 Hours

Relaunch the diagnostic script from the previous section. If your measures are effective, you should see:

  • Event 4625: < 100/day (vs thousands before).
  • Top attacking IPs: 1-3 attempts each (vs hundreds).
  • IPBan firewall rules: steady growth of banned IPs.

Email Alert in Case of Massive Attack

Script to schedule every hour to alert if > 500 failures/hour:

$count = (Get-WinEvent -FilterHashtable @{
    LogName='Security'; ID=4625; StartTime=(Get-Date).AddHours(-1)
} -ErrorAction SilentlyContinue | Measure-Object).Count

if ($count -gt 500) {
    Send-MailMessage -From "[email protected]" -To "[email protected]" `
        -Subject "⚠️ Ongoing RDP Attack: $count attempts in 1h" `
        -Body "VPS targeted. Check IPBan and logs." `
        -SmtpServer "smtp.yourservice.com"
}

Weekly Account Audit

Once a week, check that no suspicious accounts have been created:

Get-LocalUser | Where-Object { $_.Enabled -eq $true } | Format-Table Name, LastLogon, Description

Monitor recently appeared accounts that you do not recognize.

FAQ - Securing RDP Against Brute-Force

How many RDP login attempts am I supposed to endure on a VPS?

On a newly deployed Windows VPS with the standard port 3389 exposed, expect 5,000 to 50,000 attempts per day from the first hours. This traffic comes from botnets continuously scanning IPv4. A reduction to less than 100/day is the goal after applying the guide.

What is the command to see the IPs attacking my RDP?

In admin PowerShell: Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625} -MaxEvents 1000 then extract the IpAddress fields from the XML. The complete script is in the diagnostic section. It gives you the top 20 IPs with the number of attempts.

IPBan or Fail2Ban4Win, which one to choose?

IPBan is more mature, better documented, and offers a Pro version with a dashboard. Fail2Ban4Win is more modern (.NET 6+), 100% open source MIT, and lighter. For most use cases, the free version of IPBan is more than sufficient. Fail2Ban4Win is better if you want to contribute to the code or integrate it into a CI.

Is changing the RDP port from 3389 to another enough to protect myself?

No, but it is useful as a complement. Changing the port eliminates 95% of the noise from automated scans (bots only scan 3389 by default). But a targeted attacker will perform a full scan of your ports in a few minutes. Combine custom port + NLA + lockout + IPBan for real protection.

How can I tell if my VPS has already been compromised?

Check: (1) list of local accounts (Get-LocalUser) - any account not created by you is suspicious; (2) scheduled tasks (Get-ScheduledTask) - look for unknown scripts; (3) running processes (Get-Process) - look for miners (xmrig, ethminer) or unusual outgoing connections; (4) Event Log Application/System - look for unexpected recent installations. If in serious doubt, reinstall the VPS from scratch - a compromised VPS cannot be cleaned, it must be replaced.

Does IP whitelisting work if I work remotely?

Poorly. If your IP changes regularly (3G/4G, hotels, coworking), whitelisting becomes impractical. Solution: set up a VPN on the VPS (WireGuard is ideal, 10 min to install) and allow RDP only from the internal IP of the VPN. You maintain protection without having to manage an IP list.

What to do if I am locked out after misconfiguring the firewall?

Use your host's out-of-band console (VNC, KVM, IPMI) - at OuiHeberg, accessible from the client area. This console provides direct access to the VPS without going through RDP, thus independent of the firewall. You can correct or disable the problematic rule.

How long does it take for a brute-force attack to succeed on a weak account?

With an 8-character lowercase password: a few minutes. With 12 mixed characters: a few days. With 14+ strong characters: several years. That’s why the password policy (net accounts /minpwlen:14) combined with account lockout makes brute-force practically impossible.

Does IPBan slow down my server?

No. IPBan runs as a lightweight service that reads the Event Log and adds rules to the native Windows Firewall. The consumption is negligible (< 30 MB RAM, < 1% CPU even under massive attack). Windows firewall rules are optimized to handle tens of thousands of IPs without noticeable impact.

What to do with banned IPs after several months?

You can periodically purge old IPs via IPBan (parameter ExpireTime). A window of 30 to 90 days is a good compromise: botnets regularly renew their IPs, not keeping them indefinitely prevents the list from becoming unmanageable.

Conclusion

Blocking RDP brute-force attacks on a Windows VPS requires three complementary layers: (1) RDP hygiene - NLA, lockout, strong passwords, custom port; (2) auto-ban with IPBan or a custom PowerShell script; (3) ideally, a VPN or bastion to not expose RDP at all.

With these three layers, you go from 20,000 attempts/day to less than 100, and the few IPs that still try are automatically banned. Your VPS remains easily accessible for you, but becomes an impenetrable wall for bots.

Do you want a Windows VPS already configured with IPBan, hardened firewall, and free out-of-band console included? OuiHeberg Windows VPS are preconfigured for production: NLA enabled, strict lockout policy, optional WireGuard VPN, 7/7 support based in France.