The Windows PowerShell script I introduce here detects brute force Remote Desktop attacks and blocks them in the Windows Firewall.

At the height of the COVID19 pandemic, many companies moved to a remote work environment. This led to a rise in brute force RDC attacks, which can prevent access to remote desktop sessions and ultimately lead to a compromised network. While most organizations utilize a frontend security appliance to detect and block these attacks, not every organization can afford the expensive subscription-based licensing they require.

This PowerShell script counts the number of IP addresses in the Windows Defender Firewall log that are attempting to connect over Remote Desktop. If there are more than 10 attempts made by an IP address over a set amount of time (5 minutes), the PowerShell script logs that IP address, transforms it into a /16 subnet, and adds the subnet(s) to a firewall rule that blocks connection attempts from those subnets.

Typically, threat actors who initiate these kinds of brute force attacks will change the IP address several times, so it is important that entire subnets are denied incoming access.

Have a look at the PowerShell script:

# RDC Brute Force Prevention
# .
# .

# Copy firewall log file
Copy-Item -LiteralPath C:\Windows\System32\LogFiles\Firewall\pfirewall.log -Destination C:\Windows\System32\LogFiles\Firewall\pfirewall-source.log

# Declare variables
$FirewallLog = 'C:\Windows\System32\LogFiles\Firewall\pfirewall-source.log'
$FirewallRule = Get-NetFirewallRule -DisplayName "RDC Brute Force Prevention"
$CSVHeader = 'Date', 'Time', 'Action', 'Protocol', 'SourceIP', 'DestIP', 'SourcePort', 'DestPort'
$CSVFile = Import-Csv -Delimiter ' ' -Path $FirewallLog -Header $CSVHeader
$Date = Get-Date -DisplayHint Date -Format "yyyy-MM-dd"
$Time = Get-Date -DisplayHint Time -Format "HH:mm:ss"
$TimeSub5 = (Get-Date -DisplayHint Time).AddMinutes(-5)
$Obj = New-Object PSObject
$Subnets = @()
$NewScope = @()

# Add firewall rule (if it doesn't already exist)
If ($FirewallRule -EQ $null) {
    # Firewall rule does not exist
    New-NetFirewallRule -DisplayName "RDC Brute Force Prevention" -Direction Inbound -Action Block -RemoteAddress '255.255.255.254'
} else {
    # Do nothing; firewall rule exists
}

# Clear existing subnets from firewall rule after 24 hours
If ($Time -LIKE "00:00:**") {
    # It's midnight
    Set-NetFirewallRule -DisplayName "RDC Brute Force Prevention" -RemoteAddress '255.255.255.254'
} else {
    # Do nothing
}

# Import CSV | Skip the first 5 lines | Criteria: 
## - Date is today
## - Time is between the last 5 minutes and now
## - Source IP is not the host
## - Destination port is 3389
## - Count the number of IPs trying to connect to 3389 in the last 5 minutes
$CSVFile | Select -Skip 5 | Where {$_.Date -EQ $Date -and $_.Time -GE $TimeSub5.ToString("HH:mm:ss") -and $_.SourceIP -NE '127.0.0.1' -and $_.DestPort -EQ '3389'} | Group-Object -Property SourceIP -NoElement | Where {$_.Count -GT '5'} | Sort-Object -Descending | % {
    # Convert counted IPs to subnets and add to $Subnets array
    ForEach-Object {
        $IPAddress = "$($_.Name)".ToString()
        $Byte = $IPAddress.Split(".")
        $Subnet = ($Byte[0] + "." + $Byte[1] + ".0.0/255.255.0.0")
        $Subnets += $Subnet
        }
    }

# Remove duplicate subnets from Subnets array
$Subnets = ($Subnets | Sort -Unique)

# Add new subnets to NewScope array
$ExistingScope = (Get-NetFirewallRule -DisplayName "RDC Brute Force Prevention" | Get-NetFirewallAddressFilter).RemoteAddress
$NewScope += $Subnets + $ExistingScope

# Remove duplicate subnets from NewScope array
$NewScope = ($NewScope | Sort -Unique)
$NewScope

# Add new subnets to firewall rule
Set-NetFirewallRule -DisplayName "RDC Brute Force Prevention" -RemoteAddress $NewScope

# Write to event log
New-EventLog -LogName Application -Source "RDC Brute Force Prevention Script" -ErrorAction SilentlyContinue
Write-EventLog -LogName Application -Source "RDC Brute Force Prevention Script" -EntryType Information -EventId 1 -Message "The following subnets have been blocked for 24 hours:`n$NewScope"

# Remove old source firewall log files
Remove-Item -Force C:\Windows\System32\LogFiles\Firewall\pfirewall-source.log 

This script performs the following actions:

  • Creates a copy of the firewall log file. Make sure you have logging enabled in Windows Defender Firewall!
  • Adds a default firewall rule called RDC Brute Force Prevention.
  • Parses the copied firewall log file and counts the number of IPs trying to connect over port 3389 within the last 5 minutes. If an IP address makes more than 10 connection attempts within those last 5 minutes, it is flagged by the script as a malicious connection. (This can be adjusted in the script.)
  • Converts each IP to a /16 subnet.
  • Removes duplicate /16 subnets.
  • Adds the newly detected subnets to the RDC Brute Force Prevention firewall rule.
  • Logs the newly added subnets to the Event Viewer.
  • Deletes the copied firewall log file.

If you want to increase the time interval that PowerShell uses to count the number of source IPs trying to connect over port 3389, you will need to change the value set for the variable $TimeSub5. For example, to increase the time interval to 10 minutes, you would change the value of .AddMinutes(-5) to .AddMinutes(-10). You should also change the variable name to mirror the interval. So, in this example, you would change the variable name to $TimeSub10.

If you want to change the default destination port from 3389 to a custom port, you will need to edit the criterion for the destination port in the command to parse the firewall log file. For example, to change the default destination port from 3389 to 1234, you would change the criterion of $_.DestPort -EQ '3389' to $_.DestPort -EQ '1234'.

Finally, if you want to change the source IP count that is considered brute force, you will need to edit the criterion for the source IP count in the command to parse the firewall log file. For example, to change the source IP count from 5 to 10, you would change the criterion of $_.Count -GT '5' to $_.Count -GT '10'.

Once you've made the necessary customizations, save the script to a file share that is accessible to all servers you would like this script to run on. Typically, I save these scripts to the NETLOGON file share.

NETLOGON share with script

NETLOGON share with script

Launch the Group Policy MMC snap-in, and open the Group Policy object that contains the computer settings for the servers to which you want to apply this guide. At this point, we need to specify the policy settings that are going to copy down the RDC Brute Force Prevention script.

Navigate to Computer Configuration\Preferences\Windows Settings\Files and add a new file. For the source file, enter the file path to the RDC Brute Force Prevention Script you saved on the network share that is accessible by Group Policy. For this guide, I will store the script remotely in \\perigon-networks.local\NETLOGON\Scripts\RDC_Brute_Force_Prevention.ps1.

For the destination file, enter the file path to the RDC Brute Force script, where it will be stored on each local server. For this guide, I will store the script locally in C:\Scripts\.

RDC Brute Force Prevention script GPO file copy

RDC Brute Force Prevention script GPO file copy

Navigate to Computer Configuration\Preferences\Control Panel Settings\Scheduled Tasks and add a New Scheduled Task (at least Windows 7).

For the Name, enter Block Brute Force RDP Attacks.

For Run as account, select NT AUTHORITY\System.

Select the option to Run whether user is logged on or not.

Select the option to Run with highest privileges.

Scheduled Task General Tab

Scheduled Task General Tab

For the trigger, create a schedule where the task starts the day it is implemented, at 12:00 pm, recurs every 1 day, every 5 minutes, and stops after 30 minutes.

Scheduled Task Trigger Tab

Scheduled Task Trigger Tab

For the action, create an action in which a program is started, the program is powershell.exe, and the arguments are -file C:\Path\To\Script. For this guide, I will use the arguments -file C:\Scripts\RDC_Brute_Force_Prevention.ps1.

Scheduled Task Action Tab

Scheduled Task Action Tab

Close the New Task wizard. Log on to one of the servers in your environment and run a gpupdate /force command. Open the Task Scheduler as an Administrator, and you will see the scheduled task.

Task Scheduler

Task Scheduler

After 5 minutes, the Block Brute Force RDP Attacks scheduled task will run. Once it completes, launch the Windows Defender Firewall with Advanced Security MMC snap-in. Under Inbound Rules, you should see a deny rule called RDC Brute Force Prevention.

RDC Brute Force Connection inbound rule

RDC Brute Force Connection inbound rule

In addition, there will be an entry in the Event Viewer with a list of subnets added to the RDC Brute Force Prevention rule, if any.

Subscribe to 4sysops newsletter!

Although a front-end security appliance is still the best way to block brute force remote desktop attacks, this script is a simple way to achieve a comparable result.

avatar
3 Comments
  1. Armando Lopez 2 years ago

    Thank you very much, very useful, Armando from Caracas, Venezuela

  2. Wyatt 5 months ago

    Hi, script would be amazing, but seems that it does not add attackers source address to rule and connections are not blocked?

    Set-NetFirewallRule : The address is invalid. Addresses may be specified as IP addresses, ranges, or subnets. Also, the following
    address keywords are allowed in certain places: LocalSubnet, DNS, DHCP, WINS, DefaultGateway, Internet, Intranet,
    IntranetRemoteAccess, PlayToDevice. Keywords can be restricted to IPv4 or IPv6 by appending a 4 or 6.
    At line:64 char:1
    + Set-NetFirewallRule -DisplayName “RDC Brute Force Prevention” -Remote …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (MSFT_NetFirewal…ystemName = “”):root/standardcimv2/MSFT_NetFirewallRule) [Set-NetF
    irewallRule], CimException
    + FullyQualifiedErrorId : HRESULT 0x80070057,Set-NetFirewallRule

  3. Jellovator 1 month ago

    Great script! Thank you! I commented out the section which removes after 24 hours, I will review those manually. I also added some code to only write to the event log if there is activity.
    Insert at line 66:
    if ($NewScope -ne ‘255.255.255.254’)
    then enclose the existing 2 lines in brackets.

Leave a reply

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

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account