diff --git a/scan.php b/scan.php index ab1a9af..f2812b3 100644 --- a/scan.php +++ b/scan.php @@ -19,38 +19,38 @@ class MalwareScanner { //Pretty Colors - private $ANSI_GREEN = "\033[32m"; - private $ANSI_RED = "\033[31m"; - private $ANSI_YELLOW = "\033[33m"; - private $ANSI_BLUE = "\033[36m"; - private $ANSI_OFF = "\033[0m"; + private $ANSI_GREEN = "\033[32m"; + private $ANSI_RED = "\033[31m"; + private $ANSI_YELLOW = "\033[33m"; + private $ANSI_BLUE = "\033[36m"; + private $ANSI_OFF = "\033[0m"; - private $dir = ''; - private $extension = '.php'; - private $flagBase64 = false; - private $flagChecksum = false; - private $flagComments = false; - private $flagHideOk = false; + private $dir = ''; + private $extension = '.php'; + private $flagBase64 = false; + private $flagChecksum = false; + private $flagComments = false; + private $flagHideOk = false; private $flagHideWhitelist = false; - private $flagNoStop = false; - private $flagPattern = false; - private $flagTime = false; - private $extraCheck = false; - private $whitelist = array(); - private $ignore = array(); - private $stat = array( - 'directories' => 0, - 'files_scanned' => 0, - 'files_infected' => 0, - ); + private $flagNoStop = false; + private $flagPattern = false; + private $flagTime = false; + private $extraCheck = false; + private $whitelist = array(); + private $ignore = array(); + private $stat = array( + 'directories' => 0, + 'files_scanned' => 0, + 'files_infected' => 0, + ); private $followSymlink = false; //Pattern File Attributes - private $patterns_raw = array(); - private $patterns_iraw = array(); - private $patterns_re = array(); + private $patterns_raw = array(); + private $patterns_iraw = array(); + private $patterns_re = array(); private $patterns_b64functions = array(); - private $patterns_b64keywords = array(); + private $patterns_b64keywords = array(); //Constructor - Likes to do as little as possible. public function __construct() @@ -65,11 +65,11 @@ class MalwareScanner //Allows the -n/--no-color flag to easily remove color characters. private function disableColor() { - $this->ANSI_GREEN = ''; - $this->ANSI_RED = ''; + $this->ANSI_GREEN = ''; + $this->ANSI_RED = ''; $this->ANSI_YELLOW = ''; - $this->ANSI_BLUE = ''; - $this->ANSI_OFF = ''; + $this->ANSI_BLUE = ''; + $this->ANSI_OFF = ''; } //Prints the passed 'string' in red text, calls showHelp(). @@ -85,19 +85,19 @@ class MalwareScanner //Handles pattern loading and saving to the class object private function initializePatterns() { - //Loads either the primary scanning patterns or the base64 patterns depending on -b/--base64 flag + $dir = dirname(__FILE__); + //Loads either the primary scanning patterns or the base64 patterns depending on -b/--base64 flag if (!$this->flagBase64) { - $this->patterns_raw = $this->loadPatterns(dirname(__FILE__) . '/definitions/patterns_raw.txt'); - $this->patterns_iraw = $this->loadPatterns(dirname(__FILE__) . '/definitions/patterns_iraw.txt'); - $this->patterns_re = $this->loadPatterns(dirname(__FILE__) . '/definitions/patterns_re.txt'); + $this->patterns_raw = $this->loadPatterns($dir . '/definitions/patterns_raw.txt'); + $this->patterns_iraw = $this->loadPatterns($dir . '/definitions/patterns_iraw.txt'); + $this->patterns_re = $this->loadPatterns($dir . '/definitions/patterns_re.txt'); + } else { + $this->patterns_b64functions = $this->loadPatterns($dir . '/base64_patterns/php_functions.txt'); + $this->patterns_b64keywords = $this->loadPatterns($dir . '/base64_patterns/php_keywords.txt'); } - else { - $this->patterns_b64functions = $this->loadPatterns(dirname(__FILE__). '/base64_patterns/php_functions.txt'); - $this->patterns_b64keywords = $this->loadPatterns(dirname(__FILE__). '/base64_patterns/php_keywords.txt'); - } - + //Adds additional checks to patterns_raw - //This may be something to move into a pattern file rather than leave hardcoded. + //This may be something to move into a pattern file rather than leave hardcoded. if ($this->extraCheck) { $this->patterns_raw['googleBot'] = '# '; $this->patterns_raw['htaccess'] = '# '; @@ -137,8 +137,8 @@ class MalwareScanner continue; } //Check if first char in pattern is a '#' which indicates a comment and skips. - //Stores the comment to be stored with the pattern in the list as key:value pairs. - //The pattern is the key and the comment is the value. + //Stores the comment to be stored with the pattern in the list as key:value pairs. + //The pattern is the key and the comment is the value. if ($pattern[0] === '#') { $last_comment = $pattern; continue; @@ -166,32 +166,34 @@ class MalwareScanner //All flag handling stuff should be setup here. private function parseArgs() { - $options = getopt( 'd:e:i:abmcxlhkwnspt', - array( - 'directory:', - 'extension:', - 'ignore:', - 'all-output', - 'base', - 'checksum', - 'comment', - 'extra-check', - 'follow-link', - 'help', - 'hide-ok', - 'hide-whitelist', - 'no-color', - 'no-stop', - 'pattern', - 'time' - )); - + $options = getopt( + 'd:e:i:abmcxlhkwnspt', + array( + 'directory:', + 'extension:', + 'ignore:', + 'all-output', + 'base', + 'checksum', + 'comment', + 'extra-check', + 'follow-link', + 'help', + 'hide-ok', + 'hide-whitelist', + 'no-color', + 'no-stop', + 'pattern', + 'time' + ) + ); + //Help Option should be first if (isset($options['help']) || isset($options['h'])) { $this->showHelp(); exit; } - + //Options that Require Additional Parameters if (isset($options['directory']) || isset($options['d'])) { $this->dir = isset($options['directory']) ? $options['directory'] : $options['d']; @@ -210,12 +212,15 @@ class MalwareScanner //Simple Flag Options if (isset($options['all-output']) || isset($options['a'])) { - $this->flagChecksum = true; $this->flagComments = true; $this->flagPattern = true; $this->flagTime = true; + $this->flagChecksum = true; + $this->flagComments = true; + $this->flagPattern = true; + $this->flagTime = true; } if (isset($options['base64']) || isset($options['b'])) { $this->flagBase64 = true; } - if (isset($options['checksum']) || isset($options['m'])) { + if (isset($options['checksum']) || isset($options['m'])) { $this->flagChecksum = true; } if (isset($options['comment']) || isset($options['c'])) { @@ -289,18 +294,20 @@ class MalwareScanner //OK if (!$found) { - if ($this->flagHideOk){return;} + if ($this->flagHideOk) { + return; + } $state = 'OK'; $hash = ' '; $state_color = $this->ANSI_GREEN; - } - //WL + } //WL elseif ($this->inWhitelist($hash)) { - if ($this->flagHideWhitelist) {return;} + if ($this->flagHideWhitelist) { + return; + } $state = 'WL'; $state_color = $this->ANSI_YELLOW; - } - //ER + } //ER else { $state = 'ER'; $state_color = $this->ANSI_RED; @@ -311,12 +318,12 @@ class MalwareScanner if ($this->flagTime) { $changed_time = filectime($path); $htime = date('H:i d-m-Y', $changed_time); - $output_string = $output_string . $this->ANSI_BLUE . $htime . $this->ANSI_OFF . ' '; + $output_string = $output_string . $this->ANSI_BLUE . $htime . $this->ANSI_OFF . ' '; } //Include Checksum/Hash if ($this->flagChecksum) { - $output_string = $output_string . $this->ANSI_BLUE . $hash . $this->ANSI_OFF . ' '; + $output_string = $output_string . $this->ANSI_BLUE . $hash . $this->ANSI_OFF . ' '; } //Append Path @@ -389,24 +396,24 @@ class MalwareScanner //Calls the load pattern and load whitelist functions //Calls the process and report functions. private function run($dir) - { + { //Make sure a directory was specified. - if ($this->dir === '') { + if ($this->dir === '') { $this->error('No directory specified'); } - + //Make sure the input is a valid directory path. $dir = rtrim($dir, '/'); if (!is_dir($dir)) { $this->error('Specified path is not a directory: ' . $dir); } - - //Load Patterns + + //Load Patterns $this->initializePatterns(); - - //Load Whitelist - $this->loadWhitelist(); - + + //Load Whitelist + $this->loadWhitelist(); + $start = time(); $this->process($dir . '/'); $this->report($start, $dir . '/'); @@ -419,18 +426,17 @@ class MalwareScanner $this->stat['files_scanned']++; $fileContent = file_get_contents($path); $found = false; - $hash = ''; + $hash = ''; $toSearch = ''; $comment = ''; if (!$this->flagBase64) { - $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_raw, $path, $found, $hash); + $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_raw, $path, $found, $hash); $this->scanLoop('scanFunc_STRI', $fileContent, $this->patterns_iraw, $path, $found, $hash); - $this->scanLoop('scanFunc_RE', $fileContent, $this->patterns_re, $path, $found, $hash); - } - else { - $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64functions, $path, $found, $hash); - $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64keywords, $path, $found, $hash); + $this->scanLoop('scanFunc_RE', $fileContent, $this->patterns_re, $path, $found, $hash); + } else { + $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64functions, $path, $found, $hash); + $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64keywords, $path, $found, $hash); } if (!$found) { @@ -442,7 +448,7 @@ class MalwareScanner return false; } - $this->stat['files_infected']++; + $this->stat['files_infected']++; return true; } @@ -472,20 +478,24 @@ class MalwareScanner //This function should accept a pattern string and a content string. //This function will return true if the pattern exists in the content. //See 'scanFunc_STR', 'scanFunc_STRI', 'scanFUNC_RE' above as examples. - + //Loops through all patterns in a file using the passed function name to determine a match. //Variables passed by reference for performance and modification access. private function scanLoop($scanFunction, &$fileContent, &$patterns, &$path, &$found, &$hash) { if (!$found || $this->flagNoStop) { foreach ($patterns as $pattern => $comment) { - //Call the function that is named in $scanFunction - //This allows multiple search/match functions to be used without duplicating the loop code. + //Call the function that is named in $scanFunction + //This allows multiple search/match functions to be used without duplicating the loop code. if ($this->$scanFunction($pattern, $fileContent)) { $found = true; - if ($hash === ''){$hash = md5($fileContent);} + if ($hash === '') { + $hash = md5($fileContent); + } $this->printPath($found, $path, $pattern, $comment, $hash); - if (!$this->flagNoStop){return;} + if (!$this->flagNoStop) { + return; + } } } } @@ -494,27 +504,26 @@ class MalwareScanner //Prints out the usage menu options. private function showHelp() { - echo 'Usage: php scan.php -d ' . PHP_EOL; - echo ' -h --help Show this help message' . PHP_EOL; - echo ' -d --directory Directory for searching' . PHP_EOL; - echo ' -e --extension File Extension to Scan' . PHP_EOL; - echo ' -i --ignore Directory of file to ignore' . PHP_EOL; - echo ' -a --all-output Enables --checksum,--comment,--pattern,--time' . PHP_EOL; - echo ' -b --base64 Scan for base64 encoded PHP keywords' . PHP_EOL; - echo ' -m --checksum Display MD5 Hash/Checksum of file' . PHP_EOL; - echo ' -c --comment Display comments for matched patterns' . PHP_EOL; - echo ' -x --extra-check Adds GoogleBot and htaccess to Scan List' . PHP_EOL; - echo ' -l --follow-symlink Follow symlinked directories' . PHP_EOL; - echo ' -k --hide-ok Hide results with \'OK\' status' . PHP_EOL; - echo ' -w --hide-whitelist Hide results with \'WL\' status' . PHP_EOL; - echo ' -n --no-color Disable color mode' . PHP_EOL; - echo ' -s --no-stop Continue scanning file after first hit' . PHP_EOL; - echo ' -p --pattern Show Patterns next to the file name' . PHP_EOL; - echo ' -t --time Show time of last file change' . PHP_EOL; + echo 'Usage: php scan.php -d ' . PHP_EOL; + echo ' -h --help Show this help message' . PHP_EOL; + echo ' -d --directory Directory for searching' . PHP_EOL; + echo ' -e --extension File Extension to Scan' . PHP_EOL; + echo ' -i --ignore Directory of file to ignore' . PHP_EOL; + echo ' -a --all-output Enables --checksum,--comment,--pattern,--time' . PHP_EOL; + echo ' -b --base64 Scan for base64 encoded PHP keywords' . PHP_EOL; + echo ' -m --checksum Display MD5 Hash/Checksum of file' . PHP_EOL; + echo ' -c --comment Display comments for matched patterns' . PHP_EOL; + echo ' -x --extra-check Adds GoogleBot and htaccess to Scan List' . PHP_EOL; + echo ' -l --follow-symlink Follow symlinked directories' . PHP_EOL; + echo ' -k --hide-ok Hide results with \'OK\' status' . PHP_EOL; + echo ' -w --hide-whitelist Hide results with \'WL\' status' . PHP_EOL; + echo ' -n --no-color Disable color mode' . PHP_EOL; + echo ' -s --no-stop Continue scanning file after first hit' . PHP_EOL; + echo ' -p --pattern Show Patterns next to the file name' . PHP_EOL; + echo ' -t --time Show time of last file change' . PHP_EOL; } } //Creates a new MalwareScanner object which does all the work. -new MalwareScanner(); -?> +new MalwareScanner(); \ No newline at end of file