#!/usr/bin/perl -w # # # panic button: keep an eye on any 'should never change' files. We'll freak # out and kill all our network interfaces if files *change*, but only # produce an email report if there are additions or removals - to remind me # to update the snapshot. # # 0.1 - no networked freeveracity support yet. I'll add this for 0.2 # # See pod after __END__ for documentation use strict; use Sys::Hostname; use Mail::Mailer; my $VERSION = 0.1; # which directories should never ever change my $these_should_never_change = { '/sbin/' => 'sbin', }; # monitor these and report any changes. my $keep_an_eye_on_these = { '/usr/bin/' => 'usr_bin', }; my @panic_report_recipients = ('root@yourhost.com'); my @general_report_recipients = ('admin@yourhost.com', 'otheradmin@yourhost.com'); my $snapfiles_dir = '/root/snaps'; my $reportfile = '/root/System_Integrity_Report.txt'; my $killswitch = '/sbin/ifconfig eth1 down'; # this gets run when panic ensues my $hostname = hostname(); my $smtp_server = 'mail.somehost.com'; my $packetgrab = '/usr/sbin/tcpdump -c 100'; # grab some network packets for recon/forensics my @other_recon_commands = ( 'last | head'); # you could add your own recon commands here my $nomail = 0; # debugging switch my $nokill = 0; # debugging switch my $report = " ========================================================================== System Integrity Report for $hostname: ".scalar(localtime)." ==========================================================================\n\n"; # verify integrity of things that should never change for(keys %$these_should_never_change) { my $path = $_; my $snapfile = $snapfiles_dir.'/'.$these_should_never_change->{$_}.'.vsf'; my $command_string = "freeveracity check $path $snapfile"; my $command_output = `$command_string`; my ($header,$details) = split /\n\n/,$command_output; # unless there are no changes, send these details off for more inspection.. if($details !~ /IDENTICAL/) { investigate_snapshot_mismatch($path,$details) } else { $report .= "$path looks ok.\n" } } # if we're still alive, nothing bad happened, so check out the non-critical # diretories for changes and build a report for(keys %$keep_an_eye_on_these) { my $path = $_; my $snapfile = $snapfiles_dir.'/'.$keep_an_eye_on_these->{$_}.'.vsf'; my $command_string = "freeveracity check $path $snapfile"; my $command_output = `$command_string`; my($header,$details) = split /\n\n/,$command_output; #unless there are no changes, log the incident... if($details !~ /IDENTICAL/) { investigate_snapshot_mismatch($path,$details) } else { $report .= "$path looks ok.\n"; } } # now mail out the report to the general report recipients my $mailer = new Mail::Mailer('smtp', Server => $smtp_server); for(@general_report_recipients) { $mailer->open({ From => 'Integrity Monitor', To => $_, Subject => 'File Integrity Report', }); print $mailer "Integrity Report for $hostname:\n\n$report\n\n"; $mailer->close; } #################### Subroutines from here down sub investigate_snapshot_mismatch { my($path,$details) = @_; my @details = split /\n/,$details; for(@details) { if($_ =~ /(Changed|Deleted|Created)/) { # something strange has happened....is it scary? if(defined($these_should_never_change->{$path}) and $1 eq 'Changed') { panic($path,$_); #time to run around and flail our arms } # oh - no, it's not scary, but it is odd. report it. else { $report .= "$path looks strange - $_ - perhaps the snapshot needs updating..\n" } } } } sub panic { my($path,$line) = @_; $report .= "CRITICAL path $path has a changed file! - time to hide!\n $line\n"; my $mail_report = "*** Data Integrity Alert!!!! *** Evasive Actions have been taken by $hostname - please come and rescue.\n\nDetails:\n"; my $mailer = new Mail::Mailer('smtp', Server => $smtp_server); # take evasive actions - call for help, do some recon, and kill the network interfaces... perform_reconaissance(); for(@other_recon_commands) { $report .= "Output of $_:\n\n".`$_`."\n\n"; } # run the user defined commands $mail_report .= $report; for(@panic_report_recipients) { next if $nomail; $mailer->open({ From => 'ALERT ALERT ALERT', To => $_, Subject => "*** Data Integrity Alarm on $hostname" }); print $mailer $mail_report; $mailer->close; } `$killswitch` unless $nokill; exit; } # we want to at least get a snapshot of our network connections so we can start to track # down our cracker's last jump point sub perform_reconaissance { $report .= "--------------------------\nReconaissance\n----------------------------\n"; $report .= "Netstat output:\n".`netstat -a`."\n\n"; # works on Linux and FreeBSD - ymmv $report .= "Who output:\n". `who`."\n\n"; # suck down some network packets for later analysis before going offline $report .= "Packet capture output ($packetgrab):\n\n".`$packetgrab`."\n\n"; } END { # write out our report just as we kick off... open(LOG,">>$reportfile") or die "cannot open $reportfile: $!"; print LOG $report,"\n\n"; close LOG; } __END__; =item Name panic_button: an automated file integrity management system built around freeveracity =item Author Steve McNabb steve@justsomeguy.com =item Synopsis panic_button was designed to allow an automated means of self-defense for a machine in production. The basic idea is that certain areas of your unix file tree should never change. Things like /sbin or /bin should be exactly the same as they were when the OS was installed. It is logical to assume, therefore, that if anything in any of these directories changes, someone has likely rooted your box. If a "panicworthy" change is detected, panic_button goes into a panic mode. During the panic phase, panic_button will log the cause of the panic, perform some reconaissance, email a report to the administrator(s) and kills off all of its network interfaces. "Hey - doesn't that leave your machine dead in the water?". Yes, it does. But if someone managed to root your box on a friday evening, would you rather have your server be unavailable for (potentially) the whole weekend, or would you rather let your cracker have unfettered access to your goodies all weekend? Personally, I'd rather field a few grumpy support calls than spend a week reconstructing a cracked server. =item Requirements Free Veracity is used to handle the actual integrity checking. It's a very impressive tool for integrity management. It's available at http://www.freeveracity.com Mail::Mailer for sending the mail - we don't want to use ourselves as an smtp server in case the mail doesn't actually get sent before the network interfaces get killed! (thanks to rhizomatic #perl folks for pointing this out to me - and for all their other ideas) =item Installation Once you have freeveracity set up, you'll need to take some snapshots of the directories you plan to monitor. See the freeveracity docs for details. You want to put all of your snaps in one directory. Set the variables in the configuration section to reflect your environment. These variables are: $these_should_never_change : a ref to a hash of 'critical' paths => snapfile prefixes $keep_an_eye_on_these : a ref to a hash of paths => snapfile prefixes to monitor, but not panic over @panic_report_recipients : who gets sent emails when we hit panic mode? @general_report_recipients : who gets sent emails about non-critical differences? $snapfiles_dir : where are your snap files? $reportfile : where should I write the log file? $killswitch : command(s) to run when we hit panic mode - after recon and $smtp_server : where is your smtp server? DO NOT USE LOCALHOST! You want an external server $packetgrab : command(s) to run in order to capture packets for forensics during panic @other_recon_commands : other commands to run for whatever puprpose during panic phase $nomail : set this to 1 if you want mail notification turned off - for debugging $nokill : set this to 1 if you don't want the killswitch to be run - for debugging Once you're configured and you've done a few test modifications, just add panic_button to your crontab and bob's yer uncle.