I had mentioned in a previous post we use Graylog tables to give more detail for tracking windows password failures. We including information on the type of logon that failed (Interactive, Network, Batch etc.) as well as why the logon failed ( bad username, bad password, password expired, workstation clock out of range etc.) Not so much a start to finish how to but rather nuts and bolts. The tables can be used anywhere, the pipeline code assumes you are using elastic beats. Happy to answer questions.
Key Links for more event id info:
- Quick reference sheet - Key details and easy reference - a good place too start
- Ultimate Windows Security Event ID Encyclopedia
- Failed Logon Specific Information
First - Table to decode type of logon. these can be found in the quick reference sheet link above or
"number","type"
"2","Interactive"
"3","Network"
"4","Batch"
"5","Service"
"7","Unlock"
"8","NetworkClearText"
"9","NewCredentials"
"10","RemoteInteractive"
"11","CachedInteractive"
Second - Table to decode reason for logon failure
NOTE: We culled the table to key factors - more detail can be found here: Event ID 4771 - Kerberos pre-auth fail and here: Event ID 4625 - Account Failed to log on
"err_code", "explanation"
"0x6", "Bad username - kerberos"
"0x7", "New computer account(?) - kerberos"
"0x9", "Administrator should reset password - kerberos"
"0xC", "Workstation Restriction - kerberos"
"0x12", "Account Disabled, expired, locked out, logon hours restriction - kerberos"
"0x17", "The users password has expired - kerberos"
"0x18", "Bad Password - kerberos"
"0x20", "Frequently logged by computer accounts - kerberos"
"0x25", "Workstation clock too far out of sync with the DCs - kerberos"
"0xC0000064", "User name does not exist - NTLM"
"0xC000006A", "User name is correct but the password is wrong - NTLM"
"0xC0000234", "User is currently locked out - NTLM"
"0xC0000072", "Account is currently disabled - NTLM"
"0xC000006F", "User tried to logon outside his day of week or time of day restrictions - NTLM"
"0xC0000070", "Workstation restriction - NTLM"
"0xC00000193", "Account expiration - NTLM"
"0xC0000071", "Expired password - NTLM"
"0xC0000133", "Clocks between DC and other computer too far out of sync - NTLM"
"0xC0000224", "User is required to change password at next logon - NTLM"
"0xC0000225", "Evidently a bug in Windows and not a risk (per ultimateITSecurity.com) - NTLM"
"0xC000015b", "The user has not been granted the requested logon type (aka logon right) at this machine - NTLM"
Here is pipeline code we use that implements these tables in password failure detection:
rule "Bad Password Detect"
when
to_string($message.winlogbeat_event_id) == "4625"
then
// Build Alert structures
// Create subject of (e-mail) alert
let subject_0 = concat("-GL| PW FAIL: ", to_string($message.winlogbeat_event_data_TargetUserName));
let subject_1 = concat(subject_0, " connecting to ");
let subject_fin = concat(subject_1, to_string($message.winlogbeat_host_name));
set_field("e-mail_subject", subject_fin);
//
// create detail of (e-mail) alert
let LogonTypeNumber = to_string($message.winlogbeat_event_data_LogonType);
let LogonTypeResult = lookup_value("winLogonType",LogonTypeNumber, 0);
let LogonTypeErr = lookup_value("WinLogonErr" ,to_string($message.winlogbeat_event_data_SubStatus), 0);
let build_mess_0 = concat("Failed Password Attempt - ", to_string($message.winlogbeat_event_data_TargetUserName));
let build_mess_1 = concat(build_mess_0, " attempting to log in to ");
let build_mess_2 = concat(build_mess_1, to_string($message.winlogbeat_event_SubjectDomainName));
let build_mess_3 = concat(build_mess_2, "-");
let build_mess_4 = concat(build_mess_3, to_string($message.winlogbeat_host_name));
let build_mess_5 = concat(build_mess_4, ". Logon Type: ");
let build_mess_6 = concat(build_mess_5, to_string(LogonTypeResult));
let build_mess_7 = concat(build_mess_6, ". Attempt came from: ");
let build_mess_8 = concat(build_mess_7, to_string($message.winlogbeat_event_data_WorkstationName));
let build_mess_9 = concat(build_mess_8, ". ERROR: ");
let build_mess_fin = concat(build_mess_9, to_string(LogonTypeErr));
set_field("e-mail_body", build_mess_fin);
route_to_stream("password_events");
end