#!/usr/bin/perl
#http://74.220.215.202/~toshmtes/s/phish.txt
use strict;
use warnings;

#####################################################
#                                                   #
#      Written by Jean L                            #
#        Credits:                                   #
#        Sean Jenkins for Cscanner                  #
#            Learned a lot from it                  #
#        ToS: for future managing of the script.    #
# $Id: phish,v 1.1 2014/01/31 18:40:14 cade Exp $                                                  #
#####################################################
#http://perldoc.perl.org/PerlIO.html
use POSIX;
use Fcntl qw( :flock :mode O_CREAT);
use fadvise;
use IO::Dirent qw(readdirent DT_DIR DT_REG DT_LNK);
use Digest::MD5 qw(md5 md5_hex md5_base64);
use File::HomeDir;
use File::Path qw(make_path remove_tree);
use Data::Dumper;
use Getopt::Long;
use Time::HiRes;
use TOSconfig;

my @files;
my @files_type_txt;
my @modify_find_results;
my @directories;
my $stats = () ;
my $skipped_file_count = 0;

#compiling regex at the start of a scrpt is faster then calling it within a subroutine more then once. Also looks cleaner.
my $HOME = File::HomeDir->my_home;
our $config = TOSconfig::config_phish();

my $unresolved = 0;  
my $self;  
my ($verbose, $debug, $write_data_to_malware, $empty, $display_line, $depth) = (0,0,0,0,0,-1);
my $path = $HOME."/public_html";
GetOptions ("verbose" => \$verbose,  
            "bug"=>\$debug,  
            "malware"=>\$write_data_to_malware,  
            "empty"=>\$empty,  
            "help" => \&help, 
            "line" => \$display_line, 
            "path=s" => \$path,
            "depth=s"   => \$depth,
            "<>" => \&help,  
) || help();  
sub help {  
    print "\033[2J";  
    print "\033[0;0H"; #jump to 0,0  
    print "  
phish(1)                           User Commands                           
  
NAME  
       phish - This scanning tool is designed to perform a full scan of all local files/folders on a server  
  
SYNOPSIS  
  
DESCRIPTION  
       This tool searches for fraudulent login sites for various things such as banks, email accounts etc.

       -h --help  
              This help page  
  
       -m, --malware    
              Writes data to a ~/malware.txt file via standard output appending the data '>>'
 
       -e, --empty  
              Empties the ~/malware.txt file, if you don't want to append to ~/malware.txt call both -e -m  

       -d, --depth
              Sets a maximum directories it will scan recrusively, default is 15.

       -l, --line
              Prints line number

  
  
  
  
  
  
  
";  
  exit 0;  
}  
sub find_readdir {
    my $dir = shift;
    my $pattern = shift;
    my $level = shift || 1;
    my $exclude_pattern = shift || qr/^Unused$/;
    
    my $max_depth;
    if ($depth > -1) {
        $max_depth = $depth;
    } else {
        $max_depth = $config->{max_depth}
    }
    if( $level > $max_depth ) { return }

    opendir my $dh, $dir
    or do {
        system("chmod 0755 $dir");
    };
    opendir $dh, $dir
    or do {
        warn "Could not open $dir: $!\n";
        return;
    };

    for my $entry ( readdirent($dh) ) {
        next if $entry->{name} =~ /^\.\.?$/;
        if( $entry->{type} == DT_DIR ) {
             if ( $entry->{name} !~ $config->{skip_directories}) {
                 find_readdir("$dir/" . $entry->{name}, $pattern, $level + 1, $exclude_pattern);
                 push(@directories, "$dir/".$entry->{name});
             } else {
                 $skipped_file_count++;
                 next;
             }
        } elsif ($entry->{type} == DT_REG) {
            if ($entry->{name} =~ $config->{skip_files}) {
                $skipped_file_count++;
                next;
            } elsif ($entry->{name} =~ $config->{skip_cache}) {
                $skipped_file_count++;
                next;
            }

        }
        next if $entry->{name} =~ $exclude_pattern;
        next unless $entry->{name} =~ $pattern;
        next if $entry->{type} == DT_DIR;
        next if $entry->{type} == DT_LNK;
        push(@files, "$dir/" . $entry->{name});
    }
    closedir $dh;
}
   
  
  
my $pkg = bless {}, __PACKAGE__;  
  
#Max Run time in seconds  
$pkg->{'secs'} = 60*60; #60 minutes  
$pkg->main();  
  
sub main {  
    $self = shift;  
    local $SIG{ALRM} = sub {   
        print "Error: Timeout!\n";  
        exit(1);   
    };  
    alarm $self->{'secs'};  
    #Start Multithreading, Maximum execution time will remain.  
    $self->start();  
    alarm 0;  
    return 0;  
}  
  
sub start {  
    my $start;  
    my $finish;  
    my $minute = 0;  
  
    if ($empty == 1) {  
        open(MALWARE, ">", $HOME."/malware.txt");  
        fadvise(*MALWARE);  
        close(MALWARE);  
    }  
    if ($write_data_to_malware == 1) {  
        open(MALWARE, ">>", $HOME."/malware.txt");  
    }  
  
    $start = Time::HiRes::time();  
    # Minor stuff  
    run_find();  
    refine_find();  
    fix_0777_0755_0644();  
    delete_temp_directories();  
    check_file_names();  
    file_content_scan();  
    Finished();  
    $finish = Time::HiRes::time();  
    while (($finish-$start) > 60) {  
        $minute++;  
        $finish -= 60;  
    }  
    print "Script took: $minute Min ".int($finish - $start)." Sec \n";  
}  
sub run_find {  
    my @files;  
    find_readdir($path, qr//, 1, $config->{skip_files});  
}  
sub fix_0777_0755_0644 {  
    if ( -e $HOME."/public_html") {  
        foreach my $file (@files) {  
            @$stats = stat($file);  
            my $mode = sprintf "%04o", S_IMODE($stats->[2]);  
            #printf "Permissions for $file are ". $mode;  
            #print Dumper(S_IMODE($mode));  
            if (-d $file ) {  
                chmod(0755,$file);  
            } elsif (-f $file) {  
                if ($file =~ $config->{extensions755}) {  
                    if ($mode ne '0755') {  
                        chmod(0755, $file);  
                    }  
                } elsif ($file =~ $config->{extensions644}) {  
                    if ($mode ne '0644') {  
                        chmod(0644, $file);  
                    }  
                }  
            }  
            if ($mode eq '0777') {  
                if (-f $file) {  
                    chmod(0644, $file);  
                } elsif (-d $file) {  
                    chmod(0755, $file);  
                }  
            }  
  
        }  
    } else {  
        print "~/public_html doesn't exist";  
    }  
}  
sub delete_temp_directories {  
    my @directories2;  
    foreach my $directory (@directories) {  
        if ($directory =~ qr/\.log|tmp\/install_|cache\/cache__/) {  
            push(@directories2, $directory);  
            chmod(0755, $directory);  
        }  
    }  
    foreach my $directory (@directories2) {  
        if (remove_tree(@directories2, 1, 1)) {  
            if ($verbose == 1) {  
                print "Deleted $directory successfully";  
            }  
        }  
    }  
}  
sub file_content_scan {  
print "\nThese files appear to contain malicious code. \nYou will want to review the files and remove the injected code from important files and/or remove unused or invalid files.\n\n" ;  
    if ($write_data_to_malware == 1) {  
        print MALWARE "\nThese files appear to contain malicious code. \nYou will want to review the files and remove the injected code from important files and/or remove unused or invalid files.\n\n";  
    }  
    foreach my $file (@files_type_txt) {
        next unless -e $file;
        if (open(FH,"<:perlio","$file")){  
            flock(FH,LOCK_EX);  
            binmode(FH);  
            my $line_ = 0;  
            while (my $line = <FH>) {  
            $line_++;  
                chomp($line);  
                if ($line =~ $config->{file_content_regex}) {  
                    if ($display_line == 1) {
                        print "$file : line $line_\n";
                    } else {
                        print "$file\n";
                    }
                    if ($write_data_to_malware == 1) {  
                        if ($display_line == 1) {
                            print MALWARE "$file : line $line_\n";
                        } else {
                            print MALWARE "$file\n";
                        }

                    }  
                    $unresolved++;  
                    last;  
                }  
            }  
            flock(FH,LOCK_UN);  
            fadvise(*FH);  
            close(FH);  
        } else {  
            print "Error:$file $!\n";  
        }  
    }  
}  
#Seems to take the longest to run.  
sub refine_find {  
    foreach my $file (@files) {  
        if (-T $file) {  
            @$stats = stat("$file");  
            #skip files larger then 1 MB  
            if ($stats->[7] > 1048576) {  
                $skipped_file_count++;  
                next;  
            }  
            push(@files_type_txt, $file);  
        }  
    }  
    if  ($debug == 1) {  
        print "skipped $skipped_file_count\n";  
    }  
  
    # sort by inode to make scan results faster.  
    sort { $$a{'inode'} <=> $$b{'inode'}} @files_type_txt;  
}  
sub check_file_names {
    print "\nScanning for phishing files by name.\n";
    if ($write_data_to_malware == 1) {
        print MALWARE "\nScanning for phishing files by name.\n";
    }
    foreach my $file (@files_type_txt) {
        if ($file =~ $config->{file_name}) {
            print "$file appears to be a phishing file\n";
            if ($write_data_to_malware == 1) {
                print MALWARE "$file appears to be a phishing file\n";
            }
        }
    }
}
  
sub Finished {  
    print "\nTotal number of files scanned: ".(scalar @files+scalar $skipped_file_count)."\n";  
    print "Total issues remain: ".$unresolved."\n";  
}  

