Site icon Franky's Web

Exchange: Block IP after failed logins

A reader asked for an article on how brute force attacks on Exchange servers can be prevented. As Exchange servers in smaller environments are often directly accessible on the Internet (e.g. via port forward) and can also be determined very quickly thanks to autodiscover, Exchange servers are very suitable for brute force attacks. In a brute force attack, the attacker attempts to guess the correct password, generating a large number of failed logins. There are essentially two ways to prevent brute force attacks. Firstly, the user account can be automatically locked after a certain number of incorrect logins. This feature is directly supported by the Active Directory. However, locking the user has been criticized because a brute force attack can lock many users, which means that the users can no longer work. Although the attacker does not guess the password, users will quickly complain if the account is locked every few minutes.

Another way is to block the attacker's IP address after a certain number of failed logins. The user account is thus not blocked, but only the attacker is prevented from guessing the password. In this article, I present two ways in which an IP with too many failed logins can be blocked.

Block IP with Windows Firewall and PowerShell

A simple way to block an IP address after too many failed logins is to use a small PowerShell script that blocks the IP address on the Windows firewall. The failed logins are logged on the Exchange Server in the security event log with the ID 4625. In addition to the user name, this event also contains the IP address from which the failed login was executed:

With a small PowerShell script, the event log can be checked for event 4625 every 5 minutes using the task scheduler. If there are too many failed logins from the same IP in the log, a Windows Firewall rule is created which blocks the IP:

I have created a simple PowerShell script which blocks the IP from event 4625 after 5 failed login attempts with a firewall rule:

#Block IP after X failed Logins
$BlockCount = 5

#Get Date and Time
$Timestamp = get-date

#Get IPs from event log (last 10 min)
$Events = get-winevent -FilterHashtable @{Logname="Security";ID=4625;StartTime=[datetime]::Today.AddMinutes(-10)} -ea 0
#Exit Script if no Events found
if (!$Events) { exit 0 }

#Get IPs from Events
$IPs = @()
foreach ($Event in $Events) {
 [xml]$XMLEvent = $Event.ToXml()
 $IPAddress = ($XMLEvent.Event.EventData.Data | where {$_.name -eq "IpAddress"} | select "#text").'#text'
 $IPs += $IPAddress
}

#Group Events
if ($IPs.count -gt 1) {
 $IPAddressCounts = $IPs | group | select count,name | where {$_.Count -ge $BlockCount}
}

#Block IPs
if ($IPAddressCounts.count -gt 1) {
 foreach ($IPAddress in $IPAddressCounts) {
  $IP = $IPAddress.Name
  $FirewallRuleName = "BlockIP:" + $IP
  $CheckFirewallRule = Get-NetFirewallRule -DisplayName $FirewallRuleName -ea 0
  if ($CheckFirewallRule) {
   #If FW Rule exists, update with current TimeStamp
   $FirewallRule = Set-NetFirewallRule -DisplayName $FirewallRuleName -Description $Timestamp
  } else {
   #Create new FW Rule
   $FirewallRule = New-NetFirewallRule -DisplayName "$FirewallRuleName" -Direction Inbound -Protocol TCP -Action Block -RemoteAddress $IP -Description $Timestamp
  }
 }
}

The script can be executed every 5 minutes with the Windows task scheduler (with the highest privileges). If you wish, you can also unblock the IP after a certain time by deleting the firewall rule after 30 minutes, for example. This small script can be used for this purpose:

#Unblock IP (Delete Firewall Rule) after X Minutes
$BlockTime = 30

#Get Date and Time
$Timestamp = get-date
$UnblockTimeStamp = $Timestamp.AddMinutes(-$BlockTime)

#Get all Firewall Rules
$FirewallRules = Get-NetFirewallRule -DisplayName "BlockIP:*"

#Unblock IPs / Delete Firewall Rules
foreach ($FirewallRule in $FirewallRules) {
 $FirewallRuleDescription = $FirewallRule.Description
 [datetime]$FirewallRuleCreatedTimeStamp = $FirewallRuleDescription
 if ($FirewallRuleCreatedTimeStamp -lt $UnblockTimeStamp) {
  $FirewallRuleName = $FirewallRule.DisplayName
  Remove-NetFirewallRule -DisplayName $FirewallRuleName
 }
}

The first script writes the time when the firewall rule was created in the comment of the rule, the second script evaluates the time and deletes the rule again after a selectable time.

The two scripts can be easily adapted to your own needs, for example you could have an e-mail sent to you when an IP has been blocked. However, the two scripts also have their disadvantages: if the script only runs every 5 minutes, an IP may only be blocked after 5 minutes. Brute force attacks usually take longer than 5 minutes

RdpGuard (chargeable)

Another option is the RdpGuard software, which is still reasonably priced at just under 80 USD for a license. RdpGuard follows a similar approach to the two scripts above, i.e. it also reacts to the 4625 event, but can also evaluate other logs and block the IPs accordingly. This means, for example, that failed logins for SMTP, POP3 and SQL Server can also be blocked:

On the website, the software advertises with the slogan "Intrusion prevention system for your Windows Server", which is perhaps "somewhat" exaggerated given the scope of performance, because in both cases it is only a kind of "fail2ban" which is known under Linux for SSH connections in particular. A demo of RdpGuard is available here:

Conclusion

Blocking an IP after failed logins is relatively simple, but only works if no reverse proxy is used in front of the Exchange server. If a reverse proxy is used, only the IP of the proxy is shown in the event logs, but not the original source IP. In my opinion, these two options are therefore well suited for small environments to block brute force attempts. For environments with a reverse proxy, this function must be implemented on the proxy, but the proxy must also perform the authentication.

Exit mobile version