
[ http://www.rootshell.com/ ]

From emoc@vortex.misterweb.com Fri Aug  7 11:42:42 1998
Date: Thu, 6 Aug 1998 14:39:48 -0500 (EST)
From: Matthew George <emoc@vortex.misterweb.com>
To: submission@rootshell.com

#!/usr/bin/perl -w

## sysmon.pl
## Author: emoc <emoc@misterweb.com>

## 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 (<PWD>) {

	# 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 (<SHADOW>) {

				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(<DF>, "\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);
}
