Good news everyone! We are under brute force attack!

The title is a tribute to Professor Farnsworth... I mentioned it because my jokes usually never land... And just to make it clear, this post is not a guidance on what to do in case of brute force attacks (bummer... eh?), it is a just testimony of my recent experience with the topic and how I leveraged Windows built-in features, free Microsoft products and Azure services to sort it out.

So, it is a quiet Friday afternoon and I feel like testing the free subscription of Operations Management Suite. I was curious to see how much data the security logs of my 5 machines would take in one week. So I deployed the agents and... Saved by the weekend bell, I kinda forgot about it.

The week after I connected to the portal, and I saw something like this:

At first I was like 20K successful authentications? Hum... Ok, I have only 10 users in the lab but I am running a s###load of scripts. So why not. But then: 23K authentication failures! And that I couldn't explain it. So I look at the detailed reports that the Audit and Security OMS package is providing by default:

It is clear that it is not me. And I was even wondering if one of my account might be compromised by now. But not really... Brute force attacks with my passwords is very unlikely to succeed. So what are those? Looking at the details, they all hit the same machine...

This brings me back to a presentation I have delivered few weeks before. I needed to connect to my lab using RDP but the only port allowed in the premises was TCP 443. So I changed the random RDP port I was using to 443. And voila! I did my demo and everybody was happy. By doing this I actually exposed by lab to an army of RDP brute force zombies. I looked at my machine and I indeed see the thousands of random attempts, all generating 4625 events in my security logs:

The problem with the 4625 coming from a failed authentication on a RDP connection is that you don't have the source IP address:

So I enabled the firewall logs on the three profiles of my machine like this:

And I was indeed seeing an IP getting crazy on the logs (the log shows port TCP 3389, my port 443 publication is in fact a done at the perimeter by a NAT/PAT device):

 2017-07-09 12:45:29 ALLOW TCP 22556 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:31 ALLOW TCP 42610 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:33 ALLOW TCP 26244 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:37 ALLOW TCP 29731 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:39 ALLOW TCP 46383 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:41 ALLOW TCP 33357 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:45 ALLOW TCP 37050 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:46 ALLOW TCP 50106 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:45:49 ALLOW TCP 40662 3389 0 - 0 0 0 - - - RECEIVE

Even a quick search on my favorite search engine revealed very quickly that this IP was already well-known for RDP brute force attacks. So I create a rule to block at on my host firewall:

Quick look at the firewall logs:

 2017-07-09 12:45:49 ALLOW TCP 40662 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:48:51 DROP TCP 40662 3389 0 - 0 0 0 - - - RECEIVE
2017-07-09 12:48:55 DROP TCP 40662 3389 0 - 0 0 0 - - - RECEIVE

Cool. It's getting dropped. That was fun, but still, next time I want to be aware of this way quicker that this! And not figure out just by checking the OMS portal because I was bored. So let's create an alert in OMS, and next time I will know right away! Very simple logic: every 5 minutes I check for the number of event 4625 for the last 5 minutes. And if it is more than 10, I send an email:

The story could end here but later in the week... This happened:

LOL - Well I kinda asked for it. So being notified is one thing... But I want it to stop too! So let's take it up a notch. Let's have the following:

  1. Create a scheduled task which runs every 5 minutes on the attacked server which check on how many events 4625 I have on the server for the last 5 minutes.
  2. If it is more than 10, then look at the firewall log to see if it is the same IPs all the time.
  3. If it is the same IP, then add it automatically to the firewall list.

So the scheduled task is a no challenge. I spare the details on it (unless you want me to elaborate).

The script is very basic, again this is not for you to copy/paste and implement in your environment, it is just sharing my rough ideas:

#Create some sort of logs
$_brute_log = "C:\Brute\Log.txt"
Function WriteLog($__msg) {
$__ts = (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
"$($__ts) - $($__msg)" | Out-File $_brute_log -Append
WriteLog "NEW ENTRY _______________"
#Filter to look for all the 4625 for the last 5 minutes
$filter = @"
<Query Id="0" Path="Security">
<Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 12544 and (EventID=4625) and TimeCreated[timediff(@SystemTime) <= 300000]]]</Select>
#Calculate the time the query take... That's for the sake it is fine as long as it takes less than 5 minutes.
$query_start = Get-Date
$querylogs = Get-WinEvent -ComputerName localhost -FilterXml $filter -ErrorAction SilentlyContinue
$query_stop = Get-Date
#Spit some logs
WriteLog "Execution time: $($query_stop - $query_start)"
WriteLog "Result Count:  $($querylogs.Count)"
#If it is highetr than 15, something fishy it happening
If ( $querylogs.Count -ge 15 ) {
#We parse the firewall logs (only the last 5000 lines)
Get-Content C:\Windows\system32\LogFiles\Firewall\pfirewall.log -Tail 5000 | `
Where-Object { $_ -like "*ALLOW*3389*"} | `
ForEach-Object { $_.Split(" ")[4]} | `
Group-Object | `
Sort-Object Count -Descending | `
ForEach-Object {
$IP = $_.Name
$count = $_.Count
#We don't care about local IPs
If ( $IP -like "10.*" ) {
WriteLog "Local IP are skipped"
} Else {
#It's not local, let's see how many attempt I have if it is more than 10 I don't like it
If ( $count -gt 10 ) {
WriteLog "$IP doesn't look good with $count hits"
$blockrule = Get-NetFirewallRule -DisplayName "Block Brute Force"
[array] $currentlist = (Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $blockrule).RemoteAddress
#Check if the IP is already on the block list
If ( $currentlist -contains $IP ) {
WriteLog "$IP is already blocked"
} ELse {
#If not, we add it to the list
WriteLog "$IP Will be added to the filter"
$currentlist += $IP
Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $blockrule | Set-NetFirewallAddressFilter -RemoteAddress $currentlist
WriteLog "$IP added to the list"
#Let's leave a message for OMS to pick up
EVENTCREATE /L APPLICATION /T ERROR /ID 666 /D "$IP had $count hits, it has been added to the Block Brute Force firewall rule"

} Else {
WriteLog "$IP has only $count hits"
} Else {
WriteLog "Not enough 4625, looking good here"

Notice the EVENTCREATE at the end? (well I know I could have done it in PowerShell...) I plant an event that I will collect with OMS and alert on:

And this is the result in my mailbox:

And the current state of my firewall rule scope:

Anyhow, that was fun  Oh wait, what about looking at where the different connection attempts I have are coming from, since I block only the most stupid attacks? Let's do some PowerBI to parse the firewall logs:

Now that is cool :)