# 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:
