#!/usr/bin/perl -w # i'm in ur procs - seein' ur payload runnin' use strict; use Mail::Sendmail; # rpmforge: perl-Mail-Sendmail.noarch # This was developed on a CentOS 4 system. # This might not be so great on other systems. # It attempts to detect unauthorized binaries that run as apache. # It also detects processes running as apache that have forked. # Email notifications are only sent for forked processes. # The user that apache runs as. my $apache_user = 'apache'; # If you have other binaries that are alloewd to run, add them here my $allowed = '^/usr/lib/ensim/frontend/eplhttpd$|^/usr/sbin/httpd$|^/usr/sbin/httpd13$'; # Careful with this setting. If you are using email alerts, it will # send one email every sleep_interval. If you're not monitoring # this mailbox and killing the infection, you can end up with a lot # of email stacked up. # At the same time, you don't want this too long or you won't catch # the malicious script. On our systems the rogue child would only # run for 10 or 15 minutes and then stop. my $sleep_interval = 60; # The email address to notify when problems are detected: my $notification_email = 'email_address@example.com'; my $sender_address = 'sender@example.com'; # Notifications will also be sent to apache_ownage.log # Check the send_alert subroutine to add more email addresses. my $debug = 0; my $ps = '/bin/ps'; my $line; my %pids; my $pid; my %parent; my %ppid_cache; my $alert = ''; while (1) { &check_apache_procs(); sleep $sleep_interval; %pids = (); %parent = (); %ppid_cache = (); $alert = ''; } sub check_apache_procs { $debug && print "pids running as apache: "; open(PS, "$ps -u apache |"); while(defined($line = )) { if ($line =~ /^\s*(\d+)\s/) { $pid = $1; $debug && print "$pid "; $pids{$pid} = $pid; } } close(PS); $debug && print "\n"; my $link; my $ppid; foreach $pid (keys %pids) { if (-e "/proc/$pid/exe") { # ensure that the actual binary running is allowed $link = readlink("/proc/$pid/exe"); if (defined($link)) { $debug && print "link is defined: $link\n"; if ($link =~ /$allowed/) { $debug && print "pid $pid: $link is ok\n"; } else { $alert .= "EXE violation: \n"; $alert .= "pid $pid: $link is not allowed to be runing\n"; $alert .= `strings /proc/$pid/cmdline`; $alert .= `date`; $alert .= "\n"; print $alert; # if you also want email notifications for EXE violations # (less reliable), then uncomment the following line: # &send_alert($alert); $alert = ''; } } else { $debug && print "link is not defined (pid does not exist?)\n"; } # Check the parent pid. $ppid = `cat /proc/$pid/status`; if (defined($ppid)) { if ($ppid =~ /^PPid:\s+(\d+)/m) { $ppid = $1; $ppid_cache{$pid} = $ppid; $debug && print "parent pid: $ppid\n"; if (defined($parent{$ppid})) { $parent{$ppid} += 1; } else { $parent{$ppid} = 1; } } else { print "couldn't find a parent pid for $pid\n"; } } } } # analyze parent pid results. foreach $ppid (keys %parent) { $link = readlink("/proc/$ppid/exe"); if (defined($link)) { $debug && print "link is defined: $link\n"; if ($link =~ /$allowed/) { $debug && print "parent pid $ppid: $link is ok\n"; } else { $alert .= "PARENT PID VIOLATION:\n"; $alert .= " $ppid: $link is not a valid parent process\n"; $alert .= `strings /proc/$ppid/cmdline`; $alert .= `date`; $alert .= "procs with ppid $ppid:\n"; foreach $pid (keys %ppid_cache) { if ($ppid_cache{$pid} == $ppid) { $link = readlink("/proc/$pid/exe"); $alert .= "pid $pid: $link has naughty ppid: $ppid\n"; } } $alert .= "\n"; &send_alert($alert); print "$alert"; $alert = ''; } } else { $debug && print "link is not defined (pid does not exist?)\n"; } # reset the counter when we are done with it. $parent{$ppid} = 0; } } # send alert sub send_alert { my $notice = $_[0]; if ((!defined($notice) || $notice !~ /./)) { $notice = "Unspecified critical error."; } my $hostname = `hostname`; $hostname =~ s/\s+$//; # mail message. my %mail = ( To => $notification_email, From => $sender_address, Message => $notice, Subject => "$hostname apache ownage" ); sendmail(%mail); # If you want to notify more than one person: # $mail{'To'} = 'admin2@example.com'; # sendmail(%mail); # $mail{'To'} = 'admin3@example.com'; # sendmail(%mail); open (LOGFILE, ">> apache_ownage.log"); print LOGFILE $alert; close LOGFILE; }