#!/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.";