#!/usr/bin/perl -w # # scan world writable and suid files # must be root to run this script # The NFS mount point checking works for solaris ONLY. use File::Find; use Getopt::Long; use strict; my $email = 'your@email.com'; # sent email about scan result my $suid_file = '/var/audit/YU.suid.log'; my $ww_file = '/var/audit/YU.world-writtable.log'; my @suid_list =(); my @ww_list =(); my @dirs_to_skip = (); # dirs to skip my @fndlist = ('/'); # top dirs to search from chomp(my $host = `hostname`); # add NFS mount to our ignore list. FOR SOLARIS ONLY chomp(@dirs_to_skip = `mount | grep remote | cut -d' ' f1`); # add /home to our ignore list push @dirs_to_skip, ('/home','/proc'); my $OPTIONS = <<"_OPTIONS"; Usage: $0 [-c|-d] -h, display this helpscreen and exit -v, show scan result -c, create the base list for suid/world-writtable, saved in suid.list and ww.list -d, scan and diff the current scan result with a base list _OPTIONS my ($opt_help,$opt_verbose,$opt_create_base,$opt_diff); GetOptions( 'h!' => \$opt_help, 'v!' => \$opt_verbose, 'c!' => \$opt_create_base, 'd!' => \$opt_diff, ) or usage('Invalid Option'); ###################### if ($opt_create_base) { scan_suid_ww(); create_base(); } elsif ($opt_diff) { if (!(-e $suid_file) or !(-e $ww_file)) { print "no base file to compare, generating base file\n" if $opt_verbose; scan_suid_ww(); create_base(); } else { scan_suid_ww(); my(@suid_diff,@ww_diff); my $email_msg = ''; if (@suid_list) { @suid_diff = make_diff($suid_file,@suid_list); $email_msg .= "\n###### Found ". scalar(@suid_diff)." new suid files ######\n\n"; $email_msg .= join "\n",@suid_diff; } if (@ww_list) { @ww_diff = make_diff($ww_file,@ww_list); $email_msg .= "\n\n###### Found ". scalar(@ww_diff) ." new world-writtable files ######\n\n"; $email_msg .= join "\n",@ww_diff; } # send email if find new suid or ww files if (@suid_diff or @ww_diff) { print $email_msg if $opt_verbose; `/usr/bin/echo "$email_msg" | /usr/bin/mailx -s "suid and ww scan difference result on $host" $email`; # update the base with the new findings. create_base(); } } } else { usage("Missing Argument: -c or -d"); } sub create_base { open F,">$suid_file" or die $!; print F join "\n", sort @suid_list; close F; open F,">$ww_file" or die $!; print F join "\n", sort @ww_list; close F; } sub make_diff { my ($file,@list) = @_; open F,$file or die "open $file error: $!\n"; my @oldlist = ; close F; chomp @oldlist; my %oldlist; @oldlist{@oldlist}=1; return grep { !exists $oldlist{$_} } @list; } sub usage { die @_, $OPTIONS; } ########## helper function ########### sub scan_suid_ww { find( \&_find_suid_ww, @fndlist ); print "no suid file found\n" if (!@suid_list && $opt_verbose); print "no world-writtable file found\n" if (!@ww_list && $opt_verbose); } sub _find_suid_ww { for (@dirs_to_skip) { if ($File::Find::name eq $_) { print "skipping $_ \n" if $opt_verbose; $File::Find::prune = 1; return; } } my ( $dev, $ino, $mode, $nlink, $uid, $gid ); if ( ( ( $dev, $ino, $mode, $nlink, $uid, $gid ) = lstat($_) )) { if ( ( ( $mode & 02 ) == 02 ) && -f && !-l ) { push( @ww_list, $File::Find::name ); } push( @suid_list, $File::Find::name ) if -u; } }