I'm playing around with sentinel queries and the hunting queries and I've across an oddity.
In the hunting queries there is a query called "Summary of Login Failures by Reason" and when you look at the code it references the Substatus value for the event log entry and maps each hex code to a text reason.
The code is listed below:
let timeframe = 1d;
| where TimeGenerated >= ago(timeframe)
| where AccountType == 'User'and EventID == 4625
| extend Reason = case(
SubStatus == '0xc000005e', 'No logon servers available to service the logon request',
SubStatus == '0xc0000062', 'Account name is not properly formatted',
SubStatus == '0xc0000064', 'Account name does not exist',
SubStatus == '0xc000006a', 'Incorrect password', SubStatus == '0xc000006d', 'Bad user name or password',
SubStatus == '0xc000006f', 'User logon blocked by account restriction',
SubStatus == '0xc000006f', 'User logon outside of restricted logon hours',
SubStatus == '0xc0000070', 'User logon blocked by workstation restriction',
SubStatus == '0xc0000071', 'Password has expired',
SubStatus == '0xc0000072', 'Account is disabled',
SubStatus == '0xc0000133', 'Clocks between DC and other computer too far out of sync',
SubStatus == '0xc000015b', 'The user has not been granted the requested logon right at this machine',
SubStatus == '0xc0000193', 'Account has expirated',
SubStatus == '0xc0000224', 'User is required to change password at next logon',
SubStatus == '0xc0000234', 'Account is currently locked out',
The weird thing to me is that if you take a look at the event log entry the hex values are listed in the Status field rather than the Substatus field unless I'm completely misreading things. If I run a straight KQL query and search the substatus field I always get 0 items.
So my question is how does this query find these records if it appears to be querying the wrong field? I must be missing something in the backend logic??