diff --git a/README.md b/README.md index db5be08..35be287 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Usage: php scan.php -d -p --pattern Show Patterns next to the file name -t --time Show time of last file change --line-number Display matching pattern line number in file + --output-format Custom defined output format ``` Ignore argument could be used multiple times and accept glob style matching ex.: "`cache*`", "`??-cache.php`" or "`/cache`" etc. @@ -38,8 +39,22 @@ Ignore argument could be used multiple times and accept glob style matching ex.: Extension argument defaults to "`.php`" and also can be used multiple times too. * `--base64` is an alternative scan mode which ignores the main pattern files and uses a large list of php keywords and functions that have been converted to base64. Slower and prone to false positives, but gives additional base64 scanning coverage. These pattern files are located in base64_patterns and were derived from php 7 keywords and functions. Not many PHP extensions are included. -* `--comment` flag will display the last comment to appear in the pattern file before the matched pattern, so documenting the pattern files is important. -* `--pattern` flag will display the pattern string that was matched. +* `--comment` flag will display the last comment to appear in the pattern file before the matched pattern, so documenting the pattern files is important. + +Output formatting +----------------- + +Default output depending on the specified parameters, but the full format is "%S %T %M # {%F} %C %P # %L" and using ANSI coloring too. + +Possible variables are: + +* `%S` - matching indicator, possible values are OK, ER, WL +* `%T` - file change time +* `%M` - file md5 hash value +* `%F` - file with path +* `%P` - pattern +* `%C` - pattern comment +* `%L` - matching pattern line number Patterns -------- @@ -56,7 +71,7 @@ Whitelisting See [whitelist.txt](https://github.com/scr34m/php-malware-scanner/blob/master/whitelist.txt) file for a predefined MD5 hash list. Only the first 32 characters are used, rest of the line ignored so feel free to leave a comment. Tools ---------- +----- **text2base64.py** diff --git a/scan.php b/scan.php index 44486fa..9a912e8 100644 --- a/scan.php +++ b/scan.php @@ -38,6 +38,7 @@ class MalwareScanner private $flagExtraCheck = false; private $flagFollowSymlink = false; private $flagLineNumber = false; + private $outputFormat = ''; private $whitelist = array(); private $ignore = array(); private $stat = array( @@ -199,7 +200,8 @@ class MalwareScanner 'no-stop', 'pattern', 'time', - 'line-number' + 'line-number', + 'output-format:' ) ); @@ -268,6 +270,11 @@ class MalwareScanner if (isset($options['line-number'])) { $this->setFlagLineNumber(true); } + if (isset($options['output-format'])) { + $this->setOutputFormat( + is_array($options['output-format']) ? $options['output-format'] : array ($options['output-format']) + ); + } } public function setExtensions(array $a) @@ -341,6 +348,11 @@ class MalwareScanner $this->flagNoStop = $b; } + public function setOutputFormat(array $format) + { + $this->outputFormat = array_shift($format); + } + // @see http://stackoverflow.com/a/13914119 private function pathMatches($path, $pattern, $ignoreCase = false) { @@ -367,21 +379,29 @@ class MalwareScanner return (bool)preg_match($expr, $path); } - /* - Formats and prints the scan result output line by line. - Depending on specified options, it will print: - -Status code - -Last Modified Time - -MD5 Hash - -File Path - -Pattern Matched - -The last comment to appear in the pattern file before this pattern - */ + /** + * Formats and prints the scan result output line by line. + * + * Depending on specified options, it will print: + * - Status code + * - Last Modified Time + * - MD5 Hash + * - File Path + * - Pattern Matched + * - The last comment to appear in the pattern file before this pattern + * - Matching line number + * + * @param $found + * @param $path + * @param $pattern + * @param $comment + * @param $hash + * @param $lineNumber + */ private function printPath($found, $path, $pattern, $comment, $hash, $lineNumber) { - $output_string = '# '; + $default_format = '%S '; - //OK if (!$found) { if ($this->flagHideOk) { return; @@ -389,55 +409,77 @@ class MalwareScanner $state = 'OK'; $hash = ' '; $state_color = $this->ANSI_GREEN; - } //WL - elseif ($this->inWhitelist($hash)) { + } elseif ($this->inWhitelist($hash)) { if ($this->flagHideWhitelist) { return; } $state = 'WL'; $state_color = $this->ANSI_YELLOW; - } //ER - else { + } else { $state = 'ER'; $state_color = $this->ANSI_RED; } - $output_string = $state_color . $output_string . $state . $this->ANSI_OFF . ' '; //Include cTime 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 . ' '; + $ctime = date('H:i d-m-Y', $changed_time); + $default_format .= '%T'; + } else { + $ctime = ''; } //Include Checksum/Hash if ($this->flagChecksum) { - $output_string = $output_string . $this->ANSI_BLUE . $hash . $this->ANSI_OFF . ' '; + $default_format .= '%M '; } - //Append Path - //'#' and {} included to prevent accidental script execution attempts + // '#' and {} included to prevent accidental script execution attempts // in the event that script output is pasted into a root terminal - $opath = '# ' . '{' . $path . '}'; - $output_string = $output_string . $opath . ' '; + $default_format .= '# {%F} '; //'#' added again as code snippets have the potential to be valid shell commands if ($found) { if ($this->flagPattern) { - $opatt = "# $pattern"; - $output_string = $output_string . $state_color . $opatt . $this->ANSI_OFF . ' '; + $default_format .= '%P '; } if ($this->flagComments) { - $output_string = $output_string . $this->ANSI_BLUE . $comment . $this->ANSI_OFF . ' '; + $default_format .= '%C '; } if ($this->flagLineNumber) { - $output_string = $output_string . '# ' . $lineNumber; + $default_format .= '# %L'; } } - $output_string = trim($output_string) . PHP_EOL; + if ($this->outputFormat) { + $map = [ + '%S' => $state, + '%T' => $ctime, + '%M' => $hash, + '%F' => $path, + '%P' => $pattern, + '%C' => $comment, + '%L' => $lineNumber, + ]; + } else { + $map = [ + '%S' => $state_color . '# ' . $state . $this->ANSI_OFF, + '%T' => $this->ANSI_BLUE . $ctime . $this->ANSI_OFF, + '%M' => $this->ANSI_BLUE . $hash . $this->ANSI_OFF, + '%F' => $path, + '%P' => $state_color . '#' . $pattern . $this->ANSI_OFF, + '%C' => $this->ANSI_BLUE . $comment . $this->ANSI_OFF, + '%L' => $lineNumber, + ]; + } - echo $output_string; + if ($this->outputFormat) { + $format = $this->outputFormat; + } else { + $format = trim($default_format); + } + + echo str_replace(array_keys($map), array_values($map), $format) . PHP_EOL; } //Recursively scales the file system.