#!/usr/bin/perl # Version 1.4 Aug 20. A # Changelog #Jul 22 2010 Added dir name functionality for some frequently found scam directories. Can't be too precise and should be checked manually #Aug 20 2010 Finds suspicious .htaccess files use warnings; use strict; use Cwd; use File::Find; use Time::HiRes qw(usleep); my $path = cwd; my $sleep; my $count; my $days; foreach (@ARGV) { $sleep = 1 if (/slow/) && print "* Going slow.\n"; $days = $1 if /(\d*)/ && print "* Suspicious keywords in files modified in the last $1 days will have more weight.\n\n"; } my %bad_files; my @bad_dirs; my %suspicious = ( shellbot => 200, tcpflood => 200, c99shell => 200, bot_list => 200, 'eval\(gzinflate\(base64_decode' => 150, #mnoo symnitelno :) 'file_get_contents\(base64_decode\(' => 150, #mnoo symnitelno :) 'eval\(gzinflate\(base64' => 100, 'exec\(\$_POST' => 100, 'system\(\$_GET' => 100, 'eval\(base64' => 150, '\$emaillist=\$_POST' => 100, '\$_POST.*cwd' => 100, '\$_GET\[\'mode' => 90, '\$_GET\[\'port' => 60, '\$_POST\[\'dir' => 60, 'popen\(\$' => 40, 'shell_exec\(\$' => 50, '/etc/passwd' => 80, 'rand\(1,65000\)' => 50, #flooder netstat => 50, 'fread\(fopen\(\$file' => 100, 'httpd.conf' => 10, 'fsockopen\(\$' => 10, 'md5_' => 10, 'ini_set\(' => 10, ); # mostly for performance but also for avoiding counting same keyword again. Read by paragraph, not by line $/ = ''; sub matchPattern { my $file = $File::Find::name; my $dir = $File::Find::dir; if ( $dir =~ /paypal|bank|banq|webscrcmd|visa|hsbc/i ) { push(@bad_dirs, $dir) unless grep( /$dir/, @bad_dirs ); } if ( $file =~ m"\.htaccess$" ) { open INPUT, '<', $file or warn "Unable to open file: $file!\n"; while (my $row = ) { my $hacked_htaccess = 'HTTP_REFERER.*google'; if ( $row =~ /$hacked_htaccess/ ) { $bad_files{ $file } = 100; last; } } close(INPUT); } if ( $file =~ m"\.php$" ) { #vajno e vseki pyt da po4va ot 0 my $probability = 0; open INPUT, '<', $file or warn "Unable to open file: $file!\n"; if ($days) { lstat($file); if (int(-M _) < $days) { $probability += 90; } } while (my $row = ) { while ( ( my $key, my $value ) = each(%suspicious) ) { if ( $row =~ /$key/ ) { $probability += $value; if ( $probability > 99 ) { $bad_files{ $file } = $probability; print $file." ".$key."\n"; #debug return; } } } } close(INPUT); } print "! Status update: $count files processed\n" if (!( ++$count % 10000 )); #show some progress for each 1000 files processed #Slow it down to prevent server overload if ($sleep) { usleep(3000); } } find( \&matchPattern, $path ); if ((keys %bad_files) || @bad_dirs) { my $key; print "\n Results in descending order:\n"; foreach $key (sort { $bad_files {$b} <=> $bad_files {$a}} keys %bad_files) { print "- $key\t$bad_files{$key}\n"; } foreach (@bad_dirs) { print "- $_ - suspicious dir name \n"; } } else { print "Nothing suspicious found.\n"; } # Remove the file so that we don't forget it at the customer account unlink 'hack-search.pl' or warn "Please, remove file manually.";