IPBan’s ProcessToRunOnBan functionality

The IPBan config file contains 2 interesting items that can trigger actions when IP addresses are banned or unbanned: ProcessToRunOnBan and ProcessToRunOnUnban.

Here’s the default config entry for ProcessToRunOnBan:


<add key="ProcessToRunOnBan" value=""/>

I decided I wanted to make the list of banned IP addresses public by writing to a web-accessible file. I tried adding the following values:


<add key="ProcessToRunOnBan" value="%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:\add-bannedip.ps1|###IPADDRESS###"/>

<add key="ProcessToRunOnUnban" value="%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:\remove-unbannedip.ps1|###IPADDRESS###"/>

The PowerShell (.ps1) scripts were really simple. The first adds banned IPs to a text file within the web root:

# Add an IP address to the list of banned IPs
param ($BannedIP)

$BannedPath = 'C:\inetpub\wwwroot\banned.txt'
Add-Content -Path $BannedPath -Value $BannedIP

The next removes unbanned IPs from the same text file:

# Remove an IP address from the list of banned IPs
param ($UnbannedIP)

$BannedPath = 'C:\inetpub\wwwroot\banned.txt'
Set-Content -Path $BannedPath (Get-Content $BannedPath | Select-String -NotMatch $UnbannedIP)

There are some flaws and a lack of error-checking in the above. The un-ban script could match IP addresses that are not identical, for example: 192.0.2.1 would match 192.0.2.10 and 192.0.2.100. Additionally, I would want to confirm that the parameter value was a valid IP address, but this was just a quick proof-of-concept.

However, I encountered an error when the next IP address was banned:

2022-01-03 00:51:35.8763|ERROR|DigitalRuby.IPBanCore.Logger|Failed to execute process C:\Program Files\IPBan\%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:\add-bannedip.ps1 192.0.2.14: System.ComponentModel.Win32Exception (2): An error occurred trying to start process 'C:\Program Files\IPBan\%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:\add-bannedip.ps1' with working directory 'C:\Program Files\IPBan\%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:'. The system cannot find the file specified.
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo )
   at System.Diagnostics.Process.StartCore(ProcessStartInfo )
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo )
   at DigitalRuby.IPBanCore.IPBanService.<>c__DisplayClass191_0.<ExecuteExternalProcessForIPAddresses>b__0() in C:\Users\Jeff\Documents\GitHub\DigitalRuby\IPBan\IPBanCore\Core\IPBan\IPBanService_Private.cs:line 591

It apparently failed to expand %SystemRoot%, so I replaced it with C:\Windows.

As mentioned in my previous post on IPBan (IPBan: fail2ban for Windows), I am using a remote config file hosted on an HTTPS server. Shortly after I made the change on the remote server, I noticed this in the logs (logfile.txt):

2022-01-03 01:36:43.3348|INFO|DigitalRuby.IPBanCore.Logger|Config file changed

It looks like IPBan automatically checks the GetUrlConfig value for updates. I confirmed that the file at C:\Program Files\IPBan\ipban.config was updated at the same time. This is excellent, previously I thought I might need to restart the IPBan service any time the configuration changed.

Unfortunately, my change still didn’t work. I encountered the following error:

2022-01-03 14:22:21.7154|ERROR|DigitalRuby.IPBanCore.Logger|Failed to execute process C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe C:\add-bannedip.ps1 192.0.2.208: System.ComponentModel.Win32Exception (2): An error occurred trying to start process 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe C:\add-bannedip.ps1' with working directory 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe C:'. The system cannot find the file specified.
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo )
   at System.Diagnostics.Process.Start(ProcessStartInfo )
   at DigitalRuby.IPBanCore.IPBanService.<>c__DisplayClass191_0.<ExecuteExternalProcessForIPAddresses>b__0() in C:\Users\Jeff\Documents\GitHub\DigitalRuby\IPBan\IPBanCore\Core\IPBan\IPBanService_Private.cs:line 591

I decided to take a closer look at line 591:

    ProcessStartInfo psi = new()
    {
        FileName = programFullPath,
        WorkingDirectory = Path.GetDirectoryName(programFullPath),
        Arguments = replacedArgs
    };
    using Process p = Process.Start(psi);

It looked to me like it was taking the path for the PowerShell executable and the PowerShell script both as the full path. I changed the config to pass in the path to the PowerShell script as part of the arguments, which made sense:

<add key="ProcessToRunOnBan" value="C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe|C:\add-bannedip.ps1 ###IPADDRESS###"/>

That worked! The next time an IP address was banned, the .ps1 script was run and added the IP address to a web-accessible file.

IPBan: fail2ban for Windows

I was looking for a tool to block IP addresses after a certain number of failed RDP login attempts, something like fail2ban but for Windows. I came across IPBan. Calling IPBan a “fail2ban for Windows” unfairly minimizes what it can do, but it can handle that task quite nicely.

As a test I installed it on a Windows 2019 Server running in Azure using the provided install instructions from the README:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/DigitalRuby/IPBan/master/IPBanCore/Windows/Scripts/install_latest.ps1'))

You don’t want to blindly run a script that downloads and installs something on your machine without looking at it first, do you? Of course not. You take a look at the code first:

https://github.com/DigitalRuby/IPBan/blob/master/IPBanCore/Windows/Scripts/install_latest.ps1

Essentially, the script does the following:

  1. Downloads a zip file
  2. Extracts the files
  3. Sets up a Windows service
  4. Suggests that you might want to edit the config by opening the config file in Notepad

I took a look at the config file and made only one change to get started: I added a CenturyLink subnet to the allow list. CenturyLink is my ISP, and I didn’t want to accidentally lock myself out. Normally you wouldn’t want to add an entire /12 of your residential ISP, but this was just a test on a temporary and disposable server:

<add key="Whitelist" value="75.160.0.0/12"/>

I noted a few other interesting things in the config file:

  1. It can also examine Linux logs (IPBan can run on Windows or Linux)
  2. It blocks failed RDP logins, but also blocks failed logins for other Windows services, such as MSSQL and Exchange
  3. It defaults to banning an IP address after 5 failed attempts, but the number of failed attempts can be configured
  4. The default ban duration is 1 day, but this can be configured
  5. The default ban duration can take into account recidivism, so that the second or third time an address is banned it can use a longer duration
  6. It can also use a username-based allow list (whitelist), so that attempted logins from usernames not on the list will ban the IP address immediately
  7. It can add blocks based on external threat intelligence feeds, and uses the Emerging Threats blocklist by default
  8. It can pull the config file from a URL, allowing you to manage the configuration of many servers from one location.

After I reviewed the config file (C:\Program Files\IPBan\ipban.config) I restarted the IPBAN service via Services GUI. You could also restart it via Powershell:

sc.exe stop IPBAN
sc.exe start IPBAN

Now that it was up-and-running, what changes did it make? I opened up Windows Defender Firewall and opened the Advanced Settings. Looking though the Inbound rules I found 4 rules prefixed with IPBAN_, three Deny rules and one Allow rule:

  1. [Deny] IPBan_Block_0
  2. [Deny] IPBan_EmergingThreats_0
  3. [Deny] IPBan_EmergingThreats_1000
  4. [Allow] IPBan_GlobalWhitelist_0

I looked at the properties for IPBan_Block_0. Already, one IP address had been banned! (I checked several hours later and only 2 additional IP addresses had been banned, so I may have gotten lucky to see one within minutes.)

It took me a while to figure out what the difference between IPBan_EmergingThreats_0 and IPBan_EmergingThreats_1000. IPBan is creating a new firewall rule after 999 entries, so the 1000th IP address or subnet from the Emerging Threats feed was added to IPBan_EmergingThreats_1000. I’ve seen some arguments online about whether or not there is a limit to the number of addresses or subnets that can be included in the scope for a Windows Defender Firewall rule, and some sources indicate there is a limit of 1000 (and I’m fairly certain the author of IPBan is one of the people arguing there is a limit).

The IPBan_GlobalWhitelist_0 contained the CenturyLink subnet that I explicitly allowed.

I was excited about pulling the configuration from a URL, so I added my configuration to https://osric.com/chris/ipban/ipban.example.config, initially with just one tweak:

<add key="UserNameWhitelist" value="chris"/>

This would, in theory, ban any IP address immediately if they used an address like admin or guest. It uses a configurable Levenshtein distance to try to avoid banning an IP address based on a typo (for example, chros instead of chris), which is a clever approach.

I then added the URL to the config file on the server itself:

<add key="GetUrlConfig" value="https://osric.com/chris/ipban/ipban.example.config"/>

After another service restart, I wanted to test to see if a login attempt with a bad username would cause an immediate block. I opened an RDP connection using the credentials admin:admin and from a VPN IP address that would be outside of the allowed CenturyLink subnet.

I did not immediately see the VPN IP address in the IPBAN_Block_0 deny rule. I checked the logs (C:\Program Files\IPBan\logfile.txt):

2021-12-31 04:35:45.3336|INFO|DigitalRuby.IPBanCore.Logger|Firewall entries updated: 
2021-12-31 04:37:45.5791|WARN|DigitalRuby.IPBanCore.Logger|Login failure: 198.51.100.101, admin, RDP, 1
2021-12-31 04:37:45.5791|INFO|DigitalRuby.IPBanCore.Logger|IP blacklisted: False, user name blacklisted: False, fails user name white list regex: False, user name edit distance blacklisted: True
2021-12-31 04:37:45.5791|WARN|DigitalRuby.IPBanCore.Logger|Banning ip address: 198.51.100.101, user name: admin, config black listed: True, count: 1, extra info: , duration: 1.00:00:00
2021-12-31 04:37:45.6024|WARN|DigitalRuby.IPBanCore.Logger|Updating firewall with 1 entries...
2021-12-31 04:37:45.6024|INFO|DigitalRuby.IPBanCore.Logger|Firewall entries updated: 198.51.100.101

There was just a delay in adding it. I checked the IPBan_Block_0 deny rule again in Windows Defender Firewall and the IP address was there. I must have been exceedingly quick, as the cycle time defaults to 15 seconds:

<add key="CycleTime" value="00:00:00:15"/>

One other change I made to my config: I set this option to false so that any future tests would not send my VPN IP to the “global ipban database”:

<add key="UseDefaultBannedIPAddressHandler" value="true" />

Normally leaving that set to true should be fine, but during testing it would be good to avoid adding your own IP address to a block list. I have not yet discovered if the global IPBan database is publicly available.

A couple other things I noted:

The ipban.config file was completely overwritten by the version at the GetUrlConfig, including the GetUrlConfig value! The next time I restarted the IPBAN service, it did not pick up the config from that URL, as the GetUrlConfig option was now blank. I updated the GetUrlConfig value on ipban.config locally and on the remotely hosted ipban.example.config to include the URL to itself, so that it would persist after a restart.

The FirewallRules config option has a somewhat confusing syntax, at least for Deny rules. I added the following rule, which then appeared in my Windows Defender Firewalls block rules as IPBan_EXTRA_GoogleDNS_0:

<add key="FirewallRules" value="
    GoogleDNS;block;8.8.4.4;53;.
"/>

This rule allows inbound port 53/tcp traffic from 8.8.4.4 and blocks it from all other ports (0-52/tcp and 54-65535/tcp). I’m still not sure how to specify that I want to block all ports, or block both TCP and UDP traffic using this config option.

IPBan has a lot of other configuration options that I’m excited to test. For me, this tool fills a major gap for Windows servers!