# Process Windows security events logged to a server, using Snare Agent or
# similar.
########################################################
## Copyright (c) 2008-2014 Orion Poplawski
## Covered under the included MIT/X-Consortium License:
## http://www.opensource.org/licenses/mit-license.php
## All modifications and contributions by other persons to
## this script are assumed to have been donated to the
## Logwatch project and thus assume the above copyright
## and licensing terms. If you want to make contributions
## under your own copyright or a different license this
## must be explicitly stated in the contribution an the
## Logwatch project reserves the right to not accept such
## contributions. If you have made significant
## contributions to this script and want to claim
## copyright please contact logwatch-devel@lists.sourceforge.net.
#########################################################
use strict;
use URI::URL;
my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
my $SuccessAudits = 0;
my %SuccessAuditUsers;
my %FailureAudits;
my %ClockSkew;
my %UnknownUser;
my %UnknownClient;
my %BadPasswords;
my %TicketExpired;
my %AccountLocked;
my %ExpiredPassword;
my %OtherList;
while (defined(my $ThisLine = <STDIN>)) {
my ($Hostname,$Criticality,$SourceName,$DateTime,$EventID,$SourceName2,$UserName,$SIDType,$EventLogType,$CategoryString,$DataString,$ExpandedString,$Extra);
#Determine format
if ($ThisLine =~ /MSWinEventLog\[/) { # Snare 4
#Parse
($Criticality,$SourceName,$DateTime,$EventID,$SourceName2,$UserName,$SIDType,$EventLogType,$Hostname,$CategoryString,$DataString,$ExpandedString,$Extra) =
($ThisLine =~ /MSWinEventLog\[(\d+)\]:(\w+)\t\d+\t([^\t]+)\t(\d+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t?([^\t]*)\t?([^\t]*)\t?([^\t]*)\t?([^\t]*)/);
} elsif ($ThisLine =~ /MSWinEventLog\t/) { # Snare 3
#Parse
($Criticality,$SourceName,$DateTime,$EventID,$SourceName2,$UserName,$SIDType,$EventLogType,$Hostname,$CategoryString,$DataString,$ExpandedString,$Extra) =
($ThisLine =~ /MSWinEventLog\t(\d+)\t(\w+)\t\d+\t([^\t]+)\t(\d+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t?([^\t]*)\t?([^\t]*)\t?([^\t]*)\t?([^\t]*)/);
}
if (!defined($Hostname)) {
print STDERR "Cannot parse $ThisLine";
next;
}
my $url = URI::URL->new("https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=$EventID");
if ($EventLogType eq "Success Audit") {
$SuccessAudits++;
$SuccessAuditUsers{$UserName}++;
}
elsif ($EventLogType eq "Failure Audit") {
if (my ($account,$domain,$reason) = ($ExpandedString =~ /^An account failed to log on\..*Account For Which Logon Failed:.*Account Name:\s+(\S+)\s+Account Domain:\s+(\S+).*Failure Reason:\s+(.+)\s+Status:.*Sub Status:/)) {
$FailureAudits{"$Hostname Log On Failure for $domain\\$account: $reason"}++;
} elsif (my ($account,$domain,$process) = ($ExpandedString =~ /^A privileged service was called\..*Account Name:\s+(\S+)\s+Account Domain:\s+(\S+).*Process Name:\s+(.+)\sService/)) {
$FailureAudits{"$Hostname Privileged service called for $domain\\$account: $process"}++ if $Detail;
} elsif ($EventID == 4768) {
# A Kerberos authentication ticket (TGT) was requested
my ($Account,$Realm,$Client,$FailureCode) = $ExpandedString =~ /Account Name:\s+(\S*)\s.*Supplied Realm Name:\s+(\S*)\s.*Client Address:\s+(\S+)\s.*Result Code:\s+(\w+)/;
if ($FailureCode eq "0x6") {
# Client not found in Kerberos database
$UnknownClient{"$Account\\$Realm $Client"}++;
} elsif ($FailureCode eq "0x17") {
# Password has expired
$ExpiredPassword{"$UserName"}++;
} else {
$FailureAudits{"$Hostname $ExpandedString\n$url"}++;
}
} elsif ($EventID == 4769) {
# A Kerberos service ticket was requested
my ($Client,$FailureCode) = $ExpandedString =~ /Client Address:\s+(\S+)\s.*Failure Code:\s+(\w+)/;
#print STDERR "EventID=$EventID Client=$Client FailureCode=$FailureCode ExpandedString=$ExpandedString\n";
if ($FailureCode eq "0x20") {
# Ticket expired
$TicketExpired{$Client}++;
} elsif ($FailureCode eq "0x25") {
# Clock skew too great
$ClockSkew{$Client}++;
} else {
$FailureAudits{"$Hostname $ExpandedString\n$url"}++;
}
} elsif ($EventID == 4771) {
# Kerberos pre-authentication failed
my ($Account,$Client,$FailureCode) = $ExpandedString =~ /Account Name:\s+(\S+)\s.*Client Address:\s+(\S+)\s.*Failure Code:\s+(\w+)/;
if ($FailureCode eq "0x12") {
#Clients credentials have been revoked Account disabled, expired, locked out, logon hours.
$AccountLocked{"$Account $Client"}++;
} elsif ($FailureCode eq "0x18") {
#Pre-authentication information was invalid - bad password
$BadPasswords{"$Account $Client"}++;
} elsif ($FailureCode eq "0x25") {
# Clock skew too great
$ClockSkew{$Client}++;
} else {
$FailureAudits{"$Hostname $ExpandedString\n$url"}++;
}
} elsif ($EventID == 4776) {
# The domain controller attempted to validate the credentials for an account
my ($Account,$Client,$FailureCode) = $ExpandedString =~ /Logon Account:\s+(\S+)\s+Source Workstation:\s+(\S+)\s.*Error Code:\s+(\w+)/;
if ($FailureCode eq "0xc0000064") {
# user name does not exist
$UnknownUser{"$Account $Client"}++;
} elsif ($FailureCode eq "0xc000006a") {
# user name is correct but the password is wrong
$BadPasswords{"$Account $Client"}++;
} elsif ($FailureCode eq "0xc0000071") {
# expired password
$ExpiredPassword{"$Account $Client"}++;
} elsif ($FailureCode eq "0xc0000234") {
# account locked
$AccountLocked{"$UserName $Client"}++;
} else {
$FailureAudits{"$Hostname $ExpandedString\n$url"}++;
}
} else {
$FailureAudits{"$Hostname $ExpandedString\n$url"}++;
}
}
else {
# Report any unmatched entries...
chomp($ThisLine);
$OtherList{$ThisLine}++;
}
}
if (keys %ClockSkew) {
print "\nClock skew too great\n";
foreach my $Client (sort keys %ClockSkew) {
print " $Client : $ClockSkew{$Client} Times\n";
}
}
if (keys %AccountLocked) {
print "\nAccount Locked\n";
foreach my $Account (sort keys %AccountLocked) {
print " $Account : $AccountLocked{$Account} Times\n";
}
}
if (keys %ExpiredPassword) {
print "\nPassword Expired\n";
foreach my $Account (sort keys %ExpiredPassword) {
print " $Account : $ExpiredPassword{$Account} Times\n";
}
}
if (keys %UnknownUser) {
print "\nUnknown Users\n";
foreach my $Account (sort keys %UnknownUser) {
print " $Account : $UnknownUser{$Account} Times\n";
}
}
if (keys %UnknownClient) {
print "\nUnknown Clients\n";
foreach my $Account (sort keys %UnknownClient) {
print " $Account : $UnknownClient{$Account} Times\n";
}
}
if (keys %BadPasswords) {
print "\nBad Passwords\n";
foreach my $Account (sort keys %BadPasswords) {
print " $Account : $BadPasswords{$Account} Times\n";
}
}
if (keys %TicketExpired) {
print "\nTicket Expired\n";
foreach my $Client (sort keys %TicketExpired) {
print " $Client : $TicketExpired{$Client} Times\n";
}
}
if (keys %FailureAudits) {
print "\nFailure Audits\n";
foreach my $Error (sort keys %FailureAudits) {
print " $Error : $FailureAudits{$Error} Times\n";
}
}
if ($SuccessAudits and ($Detail >=0) ) {
print "\nSuccess Audits " . $SuccessAudits . " Time(s)\n";
foreach my $User (keys %SuccessAuditUsers) {
print " $User : $SuccessAuditUsers{$User} Times\n";
}
}
if (keys %OtherList) {
print "\n**** Unmatched entries ****\n";
foreach my $Error (keys %OtherList) {
print " $Error : $OtherList{$Error} Times\n";
}
}
exit(0);
# vi: shiftwidth=3 tabstop=3 syntax=perl et
# Local Variables:
# mode: perl
# perl-indent-level: 3
# indent-tabs-mode: nil
# End: