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.
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:
- Have different Threshold based on the logon type
- 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: https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4625) you’ll notice it has a Logon Type. You can find the detail of what these Logon Type code means on the page https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4624:
|2||Interactive (logon at keyboard and screen of system)|
|3||Network (i.e. connection to shared folder on this computer from elsewhere on network)|
|4||Batch (i.e. scheduled task)|
|5||Service (Service startup)|
|7||Unlock (i.e. unnattended workstation with password protected screen saver)|
|8||NetworkCleartext (Logon with credentials sent in the clear text. Most often indicates a logon to IIS with “basic authentication”) See this article for more information.|
|9||NewCredentials 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.”|
|10||RemoteInteractive (Terminal Services, Remote Desktop or Remote Assistance)|
|11||CachedInteractive (logon with cached domain credentials such as when logging on to a laptop when away from the network)|
Based on this, we created 2 different alerts with 2 different threshold:
- 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.
- 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: https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=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: https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4625. Here is the table:
|0XC000005E||There are currently no logon servers available to service the logon request.|
|0xC0000064||User logon with misspelled or bad user account|
|0xC000006A||User logon with misspelled or bad password|
|0XC000006D||The cause is either a bad username or authentication information|
|0XC000006E||Indicates a referenced user name and authentication information are valid, but some user account restriction has prevented successful authentication (such as time-of-day restrictions).|
|0xC000006F||User logon outside authorized hours|
|0xC0000070||User logon from unauthorized workstation|
|0xC0000071||User logon with expired password|
|0xC0000072||User logon to account disabled by administrator|
|0XC00000DC||Indicates the Sam Server was in the wrong state to perform the desired operation.|
|0XC0000133||Clocks between DC and other computer too far out of sync|
|0XC000015B||The user has not been granted the requested logon type (also called the logon right) at this machine|
|0XC000018C||The logon request failed because the trust relationship between the primary domain and the trusted domain failed.|
|0XC0000192||An attempt was made to logon, but the Netlogon service was not started.|
|0xC0000193||User logon with expired account|
|0XC0000224||User is required to change password at next logon|
|0XC0000225||Evidently a bug in Windows and not a risk|
|0xC0000234||User logon with account locked|
|0XC00002EE||Failure Reason: An Error occurred during Logon|
|0XC0000413||Logon 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.|
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.