#!/usr/bin/perl -w ## sysmon.pl ## Author: emoc ## This script, run on a regular (daily) basis, keeps tabs ## on root accounts and set[ug]id root files. Output includes: ## list of all uid/gid 0 (or 131072) accounts ## list of all set[ug]id 0 files ## root accounts that have been added, changed, or deleted since last run ## set[ug]id 0 files that have been added, changed (incl. size), or deleted since last run ## By default, it will mail the results to root. ## It would be best to invoke it from root's cron using: ## 0 0 * * * /path/to/sysmon.pl ######################## ### User Definitions ### ######################## # [0/1] Do we have a shadow password setup (assumes /etc/shadow)? # Enabling this means that the script will process information from /etc/shadow. # It will enable the script to see if passwords on root accounts have changed, # but if you don't want it touching the shadow file, just set to 0 $USE_SHADOW = 1; # [0/1] This will disable the mail to root and simply display the # results of the script to STDOUT $NOMAIL = 0; ############ ### Code ### ############ # open /etc/passwd (and /etc/shadow if defined above) and pull all root acct info open(PWD, "/etc/passwd") || die("open /etc/passwd: $!"); while () { # also checks for uid / gid 131072, which can be interpreted as 0 if ((/.*:0:.*/) || (/.*:131072:.*/)) { chomp; $data = $_; s/:.*//; $login = $_; #D cpinfo -> npinfo $npinfo{$login} = $data; if ($USE_SHADOW) { open(SHADOW, "/etc/shadow") || die("open /etc/shadow: $!"); while () { if (/$login/) { chomp; $nsinfo{$login} = $_; #D cpinfo -> npinfo on rt of = $npinfo{$login} = $npinfo{$login} . "\n" . $_ } } close(SHADOW) || die("close /etc/shadow: $!"); #D } else { #D $npinfo{$login} = $cpinfo{$login} } } } close(PWD) || die("close /etc/passwd: $!"); ### END Acct. checks ### BEGIN set[ug]id 0 checks sub checkperms { if ( -u $file) { $uid = (stat($file))[4]; if ($uid == 0) { $nsulist{$file} = `ls -dl $file`; chomp($nsulist{$file}); return 1; } } elsif ( -g $file) { $gid = (stat($file))[5]; if ($gid == 0) { $nsglist{$file} = `ls -dl $file`; chomp($nsglist{$file}); return 1; } } return 0; } $dirlist[0] = ""; foreach $dir (@dirlist) { foreach $file (<${dir}/*>) { # turn all those nasty little quotes and ticks in filenames into literals that sh can parse a bit more cleanly $file =~ s/\'/\\\'/; $file =~ s/\"/\\\"/; $file =~ s/\`/\\\`/; # directories (excluding proc) that aren't links if (( -d $file) && ($file ne "/proc") && (! -l $file)) { checkperms(); push(@dirlist, $file); # set[ug]id files that aren't links } elsif ((( -u $file) || ( -g $file)) && (! -l $file)) { checkperms(); } } # same with everything that starts w/ a . foreach $file (<${dir}/.*>) { # make sure we aren't looking at */. or */.. as well if (( -d $file) && (! -l $file) && (! (($file =~ m#/\.$#) || ($file =~ m#/(\.)\1$#)))) { checkperms(); push(@dirlist, $file); } elsif ((( -u $file) || ( -g $file)) && ((! -l $file) && (! (($file =~ m#/\.$#) || ($file =~ m#/(\.)\1$#))))) { checkperms(); } } } if (! $NOMAIL) { open(MAIL, "|mail root") || die("open mail: $!"); select(MAIL); } dbmopen(%pinfo, "/var/log/pinfo", 0600) || die("dbmopen pinfo: $!"); print("="x20, "\n"); print("Root accounts which have been added or changed since last check:\n\n"); foreach $login (sort keys %npinfo) { if (! $pinfo{$login}) { print("$login (old):\n", "NON-EXISTANT\n\n"); print("$login (new):\n", "$npinfo{$login}\n\n"); $pinfo{$login} = $npinfo{$login}; } elsif ($npinfo{$login} ne $pinfo{$login}) { print("$login (old):\n"); print("$pinfo{$login}\n\n"); print("$login (new):\n"); print("$npinfo{$login}\n\n"); $pinfo{$login} = $npinfo{$login}; } } print("="x20, "\n"); print("Root accounts which have been deleted since last check:\n"); foreach $login (sort keys %pinfo) { if (! $npinfo{$login}) { print("$login:\n"); print("$pinfo{$login}\n\n"); delete($pinfo{$login}); } } print("\n"); dbmclose(%pinfo); dbmopen(%sulist, "/var/log/sulist", 0600); print("="x20, "\n"); print("Files that have changed or had setuid privileges added since last check:\n"); foreach $file (sort keys %nsulist) { if (! $sulist{$file}) { print("$file (old):\n"); print("NON-EXISTANT\n"); print("$file (new):\n"); print($nsulist{$file}, "\n\n"); $sulist{$file} = $nsulist{$file}; } elsif ($nsulist{$file} ne $sulist{$file}) { print("$file (old):\n"); print($sulist{$file}, "\n"); print("$file (new):\n"); print($nsulist{$file}, "\n\n"); $sulist{$file} = $nsulist{$file}; } } print("\n"); print("="x20, "\n"); print("Files that have been moved, renamed, deleted, or had setuid privileges dropped:\n"); foreach $file (sort keys %sulist) { if (! $nsulist{$file}) { print("$file (old):\n"); print($sulist{$file}, "\n"); print("$file (new):\n"); if ( -e $file) { $nv = `ls -dl $file`; chomp($nv); print($nv, "\n\n") } else { print("MOVED, RENAMED, OR DELETED\n\n"); } delete($sulist{$file}); } } print("\n"); dbmclose(%sulist); dbmopen(%sglist, "/var/log/sglist", 0600); print("="x20, "\n"); print("Files that have changed or had setgid privileges added since last check:\n"); foreach $file (sort keys %nsglist) { if (! $sglist{$file}) { print("$file (old):\n"); print("NON-EXISTANT\n"); print("$file (new):\n"); print($nsglist{$file}, "\n\n"); $sglist{$file} = $nsglist{$file}; } elsif ($nsglist{$file} ne $sglist{$file}) { print("$file (old):\n"); print($sglist{$file}, "\n"); print("$file (new):\n"); print($nsglist{$file}, "\n\n"); $sglist{$file} = $nsglist{$file}; } } print("\n"); print("="x20, "\n"); print("Files that have been moved, renamed, deleted, or had setgid privileges dropped:\n"); foreach $file (sort keys %sglist) { if (! $nsglist{$file}) { print("$file (old):\n"); print($sglist{$file}, "\n"); print("$file (new):\n"); if ( -e $file) { $nv = `ls -dl $file`; chomp($nv); print($nv, "\n\n") } else { print("MOVED, RENAMED, OR DELETED\n\n"); } delete($sglist{$file}); } } print("\n"); dbmclose(%sglist); print("="x20, "\n"); open(DF, "df|"); print(, "\n"); close(DF); print("="x20, "\n"); print("Users with either uid or gid 0 or 131072:\n\n"); #D cpinfo -> npinfo foreach (sort keys %npinfo) { if ($USE_SHADOW) { #D cpinfo -> npinfo print($_, "\n", $npinfo{$_}, "\n", $nsinfo{$_}, "\n\n") } else { #D cpinfo -> npinfo print($_, "\n", $npinfo{$_}, "\n\n") } } print("="x20, "\n"); print("setuid root files:\n"); foreach (sort keys %nsulist) { print("$nsulist{$_}\n"); } print("\n"); print("="x20, "\n"); print("setgid root files that aren\'t setuid:\n"); foreach (sort keys %nsglist) { print("$nsglist{$_}\n"); } print("\n"); if (! $NOMAIL) { close(MAIL); }