Restrict RDP Access by IP Address with Windows Firewall

You can restrict RDP access to your server, either by IP address or a range of IP addresses, using the rules in the Windows firewall. You can actually use this same method for any open port, on any Windows PC, running Windows firewall.

One thing before we start. If you are thinking about using this so that you can open RDP up to the internet, Don’t. Even though it would work, always try to use a VPN first it’s much safer. You only have to check out the Shodan website to see the internet is being scanned 24/7 and it won’t be long before your open RDP is being Brute Forced.

If your interested in finding out how easy it is to perform a Brute force attack, check out my tutorial on Brute-Forcing passwords with THC Hydra.

I have recently seen a large increase in the number of views I am getting on this tutorial. Guessing, due to lots of people working from home since Covid 19. So I have added a nice PowerShell Automation script after the manual setup, which will automatically block IPs after a specified amount of failed logins and will hopefully help someone out.

Manual Setup

This Manual setup runs you through creating the firewall rule, blocking RDP port 3389, through the windows GUI. This while being remotely connected to the Windows device.

The problem is when you create the block rule there are no options to specify which IPs you want to block until the rule is created. This effectively blocks your current connection and no one wants to have to talk someone through physically accessing a server to remove the rule and regain RDP access. However, to maintain your existing Remote Desktop connection you need to create an allow rule first. Once the rule is created add the IP addresses you want to block then you can change the rule from allow to block.

Create The Firewall Rule

  • Open Windows Defender Firewall from Control Panel and click Advanced Security.
Windows Firewall Advanced Settings
  • Click on Inbound Rules.
This image has an empty alt attribute; its file name is inboundrules.png
  • Then in the Actions Menu Click New Rule.
This image has an empty alt attribute; its file name is NewRule.png
  • Select Port and click next.
This image has an empty alt attribute; its file name is port.png
  • Add 3389 to the specific local ports, Click next.
This image has an empty alt attribute; its file name is localport.png
  • Leave this set to Allow the Connection, Click next.
This image has an empty alt attribute; its file name is allow.png

As I said earlier, If we were to set this to Block the connection now, it would block our remote session because you cant specify any connections until the rule is created.

  • Leave Domain, Private and Public all checked and click Next.
This image has an empty alt attribute; its file name is Profile.png
  • Give the Rule a name such as “CUSTOM RDP BLOCK” and Optionally a Description, then click Finish.
This image has an empty alt attribute; its file name is NameRule.png
  • Your new firewall rule is created and should now be at the top of the rules list.
This image has an empty alt attribute; its file name is ruleCreated.png

Creating Your IP Restrictions

  • Right click the Rule you just created and click properties.
This image has an empty alt attribute; its file name is RulesPropertys.png
  • Click on the Scope tab.
This image has an empty alt attribute; its file name is rulescope.png
  • Under the Remote IP address section, select These IP addresses and click Add.
This image has an empty alt attribute; its file name is RuleRemoteIpAddress.png
  • From here you can choose an IP address, address range or a subnet which you would like to block.
This image has an empty alt attribute; its file name is rulesIPorRange.png

Adding Single addresses is fine but can be a little time consuming if you’re trying to block addresses Add-Hoc.

This image has an empty alt attribute; its file name is singleIP.png

An easier and more secure option is to go with a range of addresses, Blocking every IP except the IP address you want to remotely connect from.

This image has an empty alt attribute; its file name is rulesRange.png
  • Once you have entered all the IPs you want to block Click OK.

Switch Windows Firewall Rule From Allow to Block

With all the IP addresses that you want to block added to the rule its time to switch the rule from Allow to Block.

  • Right Click the Firewall Rule again and select Properties.
This image has an empty alt attribute; its file name is RulesPropertys-1.png
  • On the General tab, under Action select Block the connection and click OK.
This image has an empty alt attribute; its file name is ruleBlock.png

If you are still remotely connected to the server after switching the rule to block. you have successfully locked down RDP without blocking your remote connection.

Remember you can use this same procedure to block access to any port with the windows firewall. however, you probably won’t need to create it as an allow rule first unless you want to maintain access to the port while it’s being configured.

Automating the Rule with PowerShell

With PowerShell and the NetFirewallRule module, you can create a script that automatically blocks IP addresses, after a specified amount of failed logins. We can even get this to run whenever a failed login event 4635 is generated.

The NetFirewallrule is only available in Server 2012\windows 8 and above if your trying to do this in Server 2008\windows7 let me know in the comments below and ill show you how to change the code using netsh advfirewall command instead.

Create the Firewall Rule

Previously, we used the Windows GUI to create our Custom RDP Block rule. However, We can make the rule even quicker using PowerShell with the NetFirewallRule module.

For some reason, you have to add two IPs to the block rule for the PowerShell script to work. Otherwise, if you use a single IP it mashes into the IPs collected from the event and you get an output like 1.1.1.1192.168.0.15. If you know why this happens, please let me know in the comments below.

  • Open an elevated PowerShell prompt and type this command.
New-NetFirewallRule -DisplayName "CUSTOM RDP BLOCK" -RemoteAddress "1.1.1.1","2.2.2.2" -Direction Inbound -Protocol TCP -Localport 3389 -Action Block
This image has an empty alt attribute; its file name is CreateRuleWithPowershellv2.png
  • You should be able to now see this rule in the inbound rules in windows Defender Firewalls, advanced Security.
This image has an empty alt attribute; its file name is image.png

The PowerShell Script

Now its time to download the PowerShell Script i created. This basically pulls the IP address out of 4625 failed logon events from event viewer and adds this to our Custom RDP Block rule.

You can download failedlogins.ps1 from the Security Tutorials Git Hub page

# Past number of hours is how meany hours back you want to search in the security log $Past_n_Hours = [DateTime]::Now.AddHours(-3)# Collect Failed login events (4625) from the security log $badRDPlogons = Get-EventLog -LogName 'Security' -after $Past_n_Hours -InstanceId 4625 | ?{$_.Message -match 'logon type:\s+(3)\s'} | Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]} }# Pull out the Ip Addresses of the failed logins$getip = $badRDPlogons | group-object -property IpAddress | where {$_.Count -gt 5} | Select -property Name# Creates Log$log = "C:\FailedLogins\rdp_blocked_ip.txt"#Takes the current IPs already in the block list$current_ips = (Get-NetFirewallRule -DisplayName "CUSTOM RDP BLOCK" | Get-NetFirewallAddressFilter ).RemoteAddress#Takes each IP captured and adds it to logforeach ($ip in $getip){$current_ips += $ip.name(Get-Date).ToString() + ' ' + $ip.name + ' The IP address has been blocked due to ' + ($badRDPlogons | where {$_.IpAddress -eq $ip.name}).count + ' attempts for 2 hours'>> $log # writing the IP blocking event to the log file}#Adds current ips to the CUSTOM RDP BLOCK ruleSet-NetFirewallRule -DisplayName "CUSTOM RDP BLOCK" -RemoteAddress $current_ips

Create the Log Folder

For the Script to work you will need to create somewhere for the log files to be saved too. By default this is C:\FailedLogins, However, this can be any location but you will also need to edit the $log location in the Failedlogins.ps1 PowerShell script.

This image has an empty alt attribute; its file name is failloginsLog.png

Copy the FaliedLogins.ps1 to this log folder once you have created It. This is optional but will make it easier to follow along with this tutorial.

Time to Test

With FailedLogins.ps1 downloaded, and the log folder created, its time to test the script.

  • Firstly Create some failed logins by connecting via RDP and typing the wrong password in.
This image has an empty alt attribute; its file name is testrdplogin.png

Remember if you are connecting remotely not to block out your connecting IP and test this from somewhere else.

  • Check the security log in event viewer for 4625 events. These are your failed logins, which the script collects and pulls the IP address from.
This image has an empty alt attribute; its file name is failedlogin4625events.png
  • Run the FailedLogins.ps1 from an administrator elevated PowerShell Prompt.
This image has an empty alt attribute; its file name is loadthescript.png

Once the script runs, the IP address you where spamming, failed logins from will be blocked and you no longer will be able to RDP from that IP.

This image has an empty alt attribute; its file name is rdpblockedFailiedlogons.png

You should also see the IP address in the “Custom RDP Block” firewall rule.

This image has an empty alt attribute; its file name is blockedRDPRulz.png

Automating the Script

Now we could just set up a scheduled task to run the script every few hours, which would work just fine or you could just run the script manually. However, my preferred method is to attach the script to run whenever a 4625 Failed login event is generated. This makes the PowerShell script, work like a very basic IDS (Intrusion Detection System).

  • Open Event Viewer and right click one of the 4625 event. Select Attach Task To This Event.
This image has an empty alt attribute; its file name is AttachFAiledLoginsToEvent.png
  • Give the Task a Name and add a description.
This image has an empty alt attribute; its file name is CreateBasicTaskFailedLogins.png
  • Click Next taking you to the When an event is Logged section. This should be all greyed out, but check that it says Event ID: 4625 in the Security log. Click Next.
This image has an empty alt attribute; its file name is confirmsecurityLogs.png
  • Make sure Start a Program is selected, click Next.
This image has an empty alt attribute; its file name is FAilledLoginsStartAProgram.png
  • Type powershell.exe as the program we want to load.
This image has an empty alt attribute; its file name is failedloginstaskpowershell.png

Add the arguments listed below to tell the task to bypass the Execution Policy and the location of the FailedLogins.ps1 script.

-ExecutionPolicy Bypass -FILE "C:\FailedLogins\FailedLogins.ps1
  • Click Next
This image has an empty alt attribute; its file name is failedloginsAddArguments.png
  • You then get a message to say your task has been created.
This image has an empty alt attribute; its file name is failedloginsSetup.png
  • Check on your new task by opening up Task Scheduler and clicking Event Viewer Tasks.
This image has an empty alt attribute; its file name is failedloginScheduledtasks.png
  • Lastly, we need to double click the task we just created. Then, select Run whether user is logged on or not and tick Do not store Password.
This image has an empty alt attribute; its file name is failedloginseditrule.png

Final Test

Now try spamming the wrong password at the RDP Connection again. This will eventually fire the script and block the IP Address from connecting any more.

This image has an empty alt attribute; its file name is rdpblockedFailiedlogons.png

Let me know in the comments below how you found it Implementing FailedLogins in your environment. Maybe you have found an easier way of implementing this or modified my PowerShell script, I want to hear from you.

If you are interested in how to perform a brute force attack on an RDP connection check out my Brute Forcing Passwords with THC Hydra Tutorial.

30 thoughts on “Restrict RDP Access by IP Address with Windows Firewall”

  1. Accidentally deleted the remote IP addresses added. When applied for “Any IP address” option and switched back to the manual entries all went deleted. Is there any scope to retrieve the whole list???

        1. One thing I do is stick the IP’s in a notepad document then you have a reference to fall back to just encase some thing like this happens again.

  2. Works like a charm. it worked for both windows 2008 and 2016 servers. Thank you very much for your great help.

    1. Thanks for the comment, glad i could help… This works on anything that has the advanced windows firewall feature which is Windows 2008 r2 and above.

      Hemp

  3. Hemp, I’m guessing the same thing will work for the Windows FTP Server – but would I need to specify any ports other than 21?

    Thanks… Bruce

  4. Hi,
    I’m hosting a local sql server and I want to allow certain local ip range
    starting from 192.168.0.1 to 192.168.0.37
    and random remote ip range for example:
    59.103.151.232, 182.176.110.205, 111.119.168.0/21
    on port 1433 (SQL)

    How can I achieve this?

    Thanks

    1. Hi Saud

      This should work for any port.

      For the local IP range when you get to point 7. do the same for the local IP addresses as stated above for the remote IP address. Adding the ranges that you want to block something like 0.0.0.0 192.168.0.0 then the next range would be 192.168.0.38 255.255.255.255 same goes for the remote addresses but you might want to consider just using the firewall on your router.

      hope this helps

      Hemp

  5. Dear

    I want to archieve to add my ISP ip adres to the scope, so the server can only accessed from my house. To stop also the failed logins each seconds showing in event viewer.
    But, my ip adress is a dynamic ip adres, I do have and implemented a dynamic name that is linked to my ISP modem/router.

    Is it possible to add my dynamic name in the scope field This Ip adresss?
    or is there another work arround ? Just to make sure I don’t block my self once the ISP Ip adress changes…. .

    1. Hi Momo Thanks for your comment,

      There is no way to add a domain name to these settings in windows firewall but you probably could do this from the firewall on your router.

      Or even better would be to close off the open RDP port and use a secure VPN to connect to your remote server.

      Hemp

    1. Hi fred

      it does work mate, make sure you have the Windows firewall service started and there is no other rule contradicting your block rule.

      Hemp

  6. Can anyone hack rdp password means main windows login of rdp ?
    i have username also of those rdp?
    anyone help me i pay for this

    1. Hi MVDK thanks for your comment.

      you still need to have a rule blocking all other IP addresses, otherwise you are just allowing all and might as well not bother with any rules..

      In reality its not a good idea to expose RDP to the internet, a better option would be to connect via a VPN first.

      Hemp 🙂

  7. So this is like the reverse of what you would normally do? (normally, you block ALL ip addresses, then add authorized ip addresses) – MS needs to rethink this – it’s overly complicated. On most firewalls, you have a list for blocked ips (usually * ), and you have a list for allowed ips. That would be a lot simpler than having to specify a range, then a gap, then another range.

    1. Hi Dave

      Thanks for your comment.

      I agree, but in reality you probably should be using your routers firewall rules to block any open ports exposed to the internet. However, this is more of a backup solution if for what ever reason you do not have any access to the router and you still want to lock down the ports.

      Hemp

  8. Hi, The Security log in Windows 2012R2 does not record the IP Address. This info is in Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational log Event ID 140. How do I rewrite the Pshell to work with this log please?

    1. Hi Gary

      Thanks For your comment.

      I actually had this same problem recently. When Windows 2012 r2 is being brute forced only no existence accounts log the IP address in the 4625 otherwise as you say the IP address is logged in the Microsoft-windows-RemoteDesktopServices-RdpCoreTS/Operational log eventID 140.

      The Problem is correlating these two logs as the EventID 140 gets logged if the event was successful or not. This his since been fixed in Windows Server 2016 however to get the script to work in Windows 2012 r2 you need to change the encryption protocol from TLS/SSL to the legacy RDP encryption.

      You will then receive a 4625 event id with the IP address but the logon type would have changed to 10 so edit the script {$_.Message -match ‘logon type:\s+(3)\s’} to {$_.Message -match ‘logon type:\s+(10)\s’}

      So its a bit of a trade-off between using the script with less secure encryption but automatically blocking IPs or a more secure login encryption protocal.

      Hope This Helps

      Hemp

      1. Hey Hemp, I am currentl trying to resolve this without changing the security settings. I have deduced it to the following workable basic to replace your bad logons:

        Get-WinEvent -FilterHashtable @{Logname=’Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational’;ID=140}

        I am now working on filtering it on ‘warning’ status or add ‘failed’ to the filter to get the correct results 🙂

        1. Hi Wannes

          I did have a go myself but could not get it working,

          if you do, i would love to see your Powershell script. so i can share it with my readers..

          Hemp

  9. Thanks!

    This helped me a lot.

    I made some changes to the ps script to avoid duplicate ip (are allowed from ps command, not from gui… ) and other minor changes. Published pull request on github.

      1. Hi, have the changes been committed? I am new to Github and if I am reading right the latest build is from October? or am I missing something? Thanks!

        1. Hi dreadrockstar

          The Changes kralizeck made to the failed Logons script have just been committed.

          sorry about the wait, I wanted to just test every thing still worked correctly, however work commitments have been taking up all my time.

          Hemp

Leave a Reply

Your email address will not be published. Required fields are marked *