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:
Essentially, the script does the following:
- Downloads a zip file
- Extracts the files
- Sets up a Windows service
- 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="220.127.116.11/12"/>
I noted a few other interesting things in the config file:
- It can also examine Linux logs (IPBan can run on Windows or Linux)
- It blocks failed RDP logins, but also blocks failed logins for other Windows services, such as MSSQL and Exchange
- It defaults to banning an IP address after 5 failed attempts, but the number of failed attempts can be configured
- The default ban duration is 1 day, but this can be configured
- 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
- 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
- It can add blocks based on external threat intelligence feeds, and uses the Emerging Threats blocklist by default
- 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:
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_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).
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 (
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:
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.
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
<add key="FirewallRules" value=" GoogleDNS;block;18.104.22.168;53;. "/>
This rule allows inbound port 53/tcp traffic from 22.214.171.124 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!