SIEM 103 — Detect Windows bruteforce part 2

by | Jun 3, 2022 | DFIR, Information Technology, InfoSec, Private Posts, SIEM, SIEM 100 series

This post is a follow up of the post SIEM 102 — Detect Windows bruteforce where I explained how to create a detection Use Case to detect a Windows bruteforce.

In this post I will explain how we can enhance the original detection logic by having a lower False Positive rate.

As I explained in the last section of the initial post, it is important to manage False Positives (FP). In the past few months, I spent some time to look for ways to reduce FP and this post will summarize them.

The context

We sometime received some alerts that we needed to investigate to understand what was happening. After the investigation, we found that it was sometime because someone mistyped their username, or it was a on a disabled account (through an automated script for example), etc. All these cases where not a bruteforce attack currently occurring on a user account.

We wanted to lower the amount of alerts we receive and only receive useful alerts.

We used 2 tactics to do so:

  1. Have different Threshold based on the logon type
  2. Adjust the query to only alert on relevant reasons

Threshold based on the logon type

If you look at what information an event for a bad logon has (event id 4625: you’ll notice it has a Logon Type. You can find the detail of what these Logon Type code means on the page

Logon TypeDescription
2Interactive (logon at keyboard and screen of system)
3Network (i.e. connection to shared folder on this computer from elsewhere on network)
4Batch (i.e. scheduled task)
5Service (Service startup)
7Unlock (i.e. unnattended workstation with password protected screen saver)
8NetworkCleartext (Logon with credentials sent in the clear text. Most often indicates a logon to IIS with “basic authentication”) See this article for more information.
9NewCredentials such as with RunAs or mapping a network drive with alternate credentials.  This logon type does not seem to show up in any events.  If you want to track users attempting to logon with alternate credentials see 4648.  MS says “A caller cloned its current token and specified new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections.”
10RemoteInteractive (Terminal Services, Remote Desktop or Remote Assistance)
11CachedInteractive (logon with cached domain credentials such as when logging on to a laptop when away from the network)
A description for all the Logon Types. Source:

Based on this, we created 2 different alerts with 2 different threshold:

  1. If the connection is from the network and non-interactive (if the Logon Type is 3), we alert if there was 25 tentative for the same user from the same source. This greatly reduces the FP for cases like Windows trying to list shares (which happens a lot). We also reduced the severity for this case.
  2. If the connection is of any other type (if the Logon Type is not 3), we alert if there was 5 tentative for the same user from the same source. The rule is “if the Logon Type is not 3” but in reality it ends up being often of either a type 2 or a type 10 meaning they are interactive. These kind of alerts do not occur often and are really reliable (even more so if we count in the tactic from the next section). We kept the original severity for this one.

Having these 2 different alerts is really useful to receive relevant alerts and knowing in one look if it’s interactive or not (we added a group by on the Logon Type so we have this information in the email we receive).

Alert only on relevant reasons

If you look at what information an event for a bad logon has (event id 4625: you’ll notice it has a Sub Status. This Sub Status gives a reason for the failed logon. You’ll notice that some of these reasons are not relevant to detect a bruteforce (for example, if the event is created for a user that does not exists, this is not a bruteforce (of course, we could create an alert to detect this specific Use Case)). However, this table is incomplete. The complete list can be found on Microsoft’s website: Here is the table:

Status\Sub-Status CodeDescription
0XC000005EThere are currently no logon servers available to service the logon request.
0xC0000064User logon with misspelled or bad user account
0xC000006AUser logon with misspelled or bad password
0XC000006DThe cause is either a bad username or authentication information
0XC000006EIndicates a referenced user name and authentication information are valid, but some user account restriction has prevented successful authentication (such as time-of-day restrictions).
0xC000006FUser logon outside authorized hours
0xC0000070User logon from unauthorized workstation
0xC0000071User logon with expired password
0xC0000072User logon to account disabled by administrator
0XC00000DCIndicates the Sam Server was in the wrong state to perform the desired operation.
0XC0000133Clocks between DC and other computer too far out of sync
0XC000015BThe user has not been granted the requested logon type (also called the logon right) at this machine
0XC000018CThe logon request failed because the trust relationship between the primary domain and the trusted domain failed.
0XC0000192An attempt was made to logon, but the Netlogon service was not started.
0xC0000193User logon with expired account
0XC0000224User is required to change password at next logon
0XC0000225Evidently a bug in Windows and not a risk
0xC0000234User logon with account locked
0XC00002EEFailure Reason: An Error occurred during Logon
0XC0000413Logon Failure: The machine you are logging on to is protected by an authentication firewall. The specified account is not allowed to authenticate to the machine.
0x0Status OK.
A description for all the Sub Status. Source:

So after analyzing all the Sub Statuses, we ended up removing all the ones we don’t care about in the context of a bruteforce detection. We ended up adding to the query: (…) and if the Sub Status is not one of:

("0XC000005E", "0XC000006E", "0xC000006F", "0xC0000070", "0xC0000071", "0xC0000072", "0XC00000DC", "0XC0000133", "0XC000015B", "0XC0000192", "0XC0000225", "0XC00002EE", "0x0", "0X80090325", "0xC0000064")

By removing all of these, we removed almost all FP and only detect true Windows bruteforce. For example, we received a total of 9 alerts in the last 90 days (at the time of writing these lines) and after verification, they were all cases where the person made an error (new employee, recently changed the password, etc.).


As you can see, any alert can be tweaked to have a really low level of False Positives while also staying relevant. We can use many tactics and only detect a true Windows bruteforce. The trick is to invest some time to analyze what information we have and think what tactics can be used to keep only relevant alerts.

Feel free to leave your comment down here for any questions or comments.


See more Posts: