Skip to content

Commit

Permalink
feat: add ignore and filter paths flag
Browse files Browse the repository at this point in the history
  • Loading branch information
marcocesarato committed Oct 5, 2020
1 parent 0eb0973 commit 3be18d3
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 48 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ Flags:
-v --version - Get version number
--max-filesize="" - Set max filesize to scan (default: -1)
--ignore-path/s="" - Ignore path/s, for multiple value separate with comma
Wildcards are enabled ex. /path/*/cache or /path/*.log
--filter-path/s="" - Filter path/s, for multiple value separate with comma
Wildcards are enabled ex. /path/*/htdocs or /path/*.php
--exploits="" - Filter exploits
--functions="" - Define functions to search
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
},
"require": {
"php": ">=5.4",
"ext-json": "*"
"ext-json": "*",
"ext-fileinfo": "*"
},
"require-dev": {
"brainmaestro/composer-git-hooks": "^2.8",
Expand Down
Binary file modified dist/scanner
Binary file not shown.
149 changes: 103 additions & 46 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Application
*
* @var string
*/
public static $version = '0.5.1.72';
public static $version = '0.5.1.74';

/**
* Root path.
Expand Down Expand Up @@ -63,14 +63,14 @@ class Application
*
* @var string
*/
public static $pathLogsInfected = '/scanner_infected.log';
public static $pathLogsInfected = '/scanner-infected.log';

/**
* Whitelist path.
*
* @var string
*/
public static $pathWhitelist = '/scanner_whitelist.csv';
public static $pathWhitelist = '/scanner-whitelist.csv';

/**
* Path to scan.
Expand Down Expand Up @@ -146,49 +146,63 @@ class Application
*
* @var int
*/
public static $summary_scanned = 0;
public static $summaryScanned = 0;

/**
* Summary detected.
*
* @var int
*/
public static $summary_detected = 0;
public static $summaryDetected = 0;

/**
* Summary removed.
*
* @var array
*/
public static $summary_removed = array();
public static $summaryRemoved = array();

/**
* Summary ignored.
*
* @var array
*/
public static $summary_ignored = array();
public static $summaryIgnored = array();

/**
* Summary edited.
*
* @var array
*/
public static $summary_edited = array();
public static $summaryEdited = array();

/**
* Summary quarantined.
*
* @var array
*/
public static $summary_quarantine = array();
public static $summaryQuarantine = array();

/**
* Summary Whitelisted.
*
* @var array
*/
public static $summary_whitelist = array();
public static $summaryWhitelist = array();

/**
* Ignore paths.
*
* @var array
*/
public static $ignorePaths = array();

/**
* Filter paths.
*
* @var array
*/
public static $filterPaths = array();

/**
* Application constructor.
Expand Down Expand Up @@ -298,6 +312,8 @@ private function arguments($args = null)
self::$argv->addFlag('whitelist-only-path', array('default' => false));
self::$argv->addFlag('max-filesize', array('default' => -1, 'has_value' => true));
self::$argv->addFlag('silent', array('default' => false));
self::$argv->addFlag('ignore-paths', array('alias' => '--ignore-path', 'default' => null, 'has_value' => true));
self::$argv->addFlag('filter-paths', array('alias' => '--filter-path', 'default' => null, 'has_value' => true));
self::$argv->addArgument('path', array('var_args' => true, 'default' => ''));
self::$argv->parse($args);

Expand Down Expand Up @@ -362,6 +378,24 @@ private function arguments($args = null)
self::$settings['log'] = false;
}

// Ignore paths
if (isset(self::$argv['ignore-paths']) && !empty(self::$argv['ignore-paths'])) {
$paths = explode(',', self::$argv['ignore-paths']);
foreach ($paths as $path) {
$path = trim($path);
self::$ignorePaths[] = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
}
}

// Filter paths
if (isset(self::$argv['filter-paths']) && !empty(self::$argv['filter-paths'])) {
$paths = explode(',', self::$argv['filter-paths']);
foreach ($paths as $path) {
$path = trim($path);
self::$filterPaths[] = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
}
}

// Check on whitelist only file path and not line number
if (isset(self::$argv['whitelist-only-path']) && self::$argv['whitelist-only-path']) {
self::$settings['whitelist-only-path'] = true;
Expand Down Expand Up @@ -542,7 +576,30 @@ public function mapping()
$directory = new RecursiveDirectoryIterator(self::$pathScan);
$files = new RecursiveIteratorIterator($directory);
$iterator = new CallbackFilterIterator($files, function ($cur) {
return $cur->isFile() && in_array($cur->getExtension(), self::$extensions, true);
$ignore = false;
$wildcard = '.*?'; // '[^\\\\\\/]*'
// Ignore
foreach (self::$ignorePaths as $ignorePath) {
$ignorePath = preg_quote($ignorePath, ';');
$ignorePath = str_replace('\*', $wildcard, $ignorePath);
if (preg_match(';' . $ignorePath . ';i', $cur->getPath())) {
$ignore = true;
}
}
// Filter
foreach (self::$filterPaths as $filterPath) {
$filterPath = preg_quote($filterPath, ';');
$filterPath = str_replace('\*', $wildcard, $filterPath);
if (!preg_match(';' . $filterPath . ';i', $cur->getPath())) {
$ignore = true;
}
}

return
!$ignore &&
$cur->isFile() &&
in_array($cur->getExtension(), self::$extensions, true)
;
});

return $iterator;
Expand Down Expand Up @@ -727,7 +784,7 @@ private function scan($iterator)

// Scanning
foreach ($iterator as $info) {
Console::progress(self::$summary_scanned, $files_count);
Console::progress(self::$summaryScanned, $files_count);

$_FILE_PATH = $info->getPathname();
$_FILE_EXTENSION = $info->getExtension();
Expand Down Expand Up @@ -765,14 +822,14 @@ private function scan($iterator)

// Scan finished

self::$summary_scanned++;
self::$summaryScanned++;
usleep(10);

if (realpath($_FILE_PATH) != realpath(__FILE__) && ($is_favicon || !empty($pattern_found)) && ($in_whitelist === 0 || $in_whitelist != count($pattern_found))) {
self::$summary_detected++;
self::$summaryDetected++;
if (self::$settings['report']) {
// Scan mode only
self::$summary_ignored[] = 'File: ' . $_FILE_PATH . PHP_EOL .
self::$summaryIgnored[] = 'File: ' . $_FILE_PATH . PHP_EOL .
'Exploits:' . PHP_EOL .
' => ' . implode(PHP_EOL . ' => ', array_keys($pattern_found));
continue;
Expand Down Expand Up @@ -828,7 +885,7 @@ private function scan($iterator)
Console::newLine();
if ($confirm2 == 'y') {
unlink($_FILE_PATH);
self::$summary_removed[] = $_FILE_PATH;
self::$summaryRemoved[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' removed!", 2, 'green');
$_WHILE = false;
}
Expand All @@ -842,7 +899,7 @@ private function scan($iterator)
}
}
rename($_FILE_PATH, $quarantine);
self::$summary_quarantine[] = $quarantine;
self::$summaryQuarantine[] = $quarantine;
Console::writeLine("File '$_FILE_PATH' moved to quarantine!", 2, 'green');
$_WHILE = false;
} elseif (in_array($confirmation, array('3')) && count($pattern_found) > 0) {
Expand Down Expand Up @@ -871,10 +928,10 @@ private function scan($iterator)
if ($confirm2 == 'y') {
Console::writeLine("File '$_FILE_PATH' sanitized!", 2, 'green');
file_put_contents($_FILE_PATH, $fc);
self::$summary_removed[] = $_FILE_PATH;
self::$summaryRemoved[] = $_FILE_PATH;
$_WHILE = false;
} else {
self::$summary_ignored[] = $_FILE_PATH;
self::$summaryIgnored[] = $_FILE_PATH;
}
} elseif (in_array($confirmation, array('4')) && count($pattern_found) > 0) {
// Remove evil line code
Expand All @@ -897,10 +954,10 @@ private function scan($iterator)
if ($confirm2 == 'y') {
Console::writeLine("File '$_FILE_PATH' sanitized!", 2, 'green');
file_put_contents($_FILE_PATH, $fc);
self::$summary_removed[] = $_FILE_PATH;
self::$summaryRemoved[] = $_FILE_PATH;
$_WHILE = false;
} else {
self::$summary_ignored[] = $_FILE_PATH;
self::$summaryIgnored[] = $_FILE_PATH;
}
} elseif (in_array($confirmation, array('5'))) {
// Edit with vim
Expand All @@ -916,9 +973,9 @@ private function scan($iterator)
break;
}
}
self::$summary_edited[] = $_FILE_PATH;
self::$summaryEdited[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' edited with vim!", 2, 'green');
self::$summary_removed[] = $_FILE_PATH;
self::$summaryRemoved[] = $_FILE_PATH;
} elseif (in_array($confirmation, array('6'))) {
// Edit with nano
$descriptors = array(
Expand All @@ -935,7 +992,7 @@ private function scan($iterator)
}
$summary_edited[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' edited with nano!", 2, 'green');
self::$summary_removed[] = $_FILE_PATH;
self::$summaryRemoved[] = $_FILE_PATH;
} elseif (in_array($confirmation, array('7'))) {
// Add to whitelist
foreach ($pattern_found as $key => $pattern) {
Expand All @@ -949,7 +1006,7 @@ private function scan($iterator)

// TODO: from char to length
if (CSV::write(self::$pathWhitelist, self::$whitelist)) {
self::$summary_whitelist[] = $_FILE_PATH;
self::$summaryWhitelist[] = $_FILE_PATH;
Console::writeLine("Exploits of file '$_FILE_PATH' added to whitelist!", 2, 'green');
$_WHILE = false;
} else {
Expand All @@ -968,7 +1025,7 @@ private function scan($iterator)
} else {
// None
Console::writeLine("File '$_FILE_PATH' skipped!", 2, 'green');
self::$summary_ignored[] = $_FILE_PATH;
self::$summaryIgnored[] = $_FILE_PATH;
$_WHILE = false;
}

Expand All @@ -988,57 +1045,57 @@ private function summary()
// Statistics
Console::displayTitle('SUMMARY', 'black', 'cyan');
Console::writeBreak();
Console::writeLine('Files scanned: ' . self::$summary_scanned);
Console::writeLine('Files scanned: ' . self::$summaryScanned);
if (!self::$settings['report']) {
self::$summary_ignored = array_unique(self::$summary_ignored);
self::$summary_edited = array_unique(self::$summary_edited);
Console::writeLine('Files edited: ' . count(self::$summary_edited));
Console::writeLine('Files quarantined: ' . count(self::$summary_quarantine));
Console::writeLine('Files whitelisted: ' . count(self::$summary_whitelist));
Console::writeLine('Files ignored: ' . count(self::$summary_ignored), 2);
self::$summaryIgnored = array_unique(self::$summaryIgnored);
self::$summaryEdited = array_unique(self::$summaryEdited);
Console::writeLine('Files edited: ' . count(self::$summaryEdited));
Console::writeLine('Files quarantined: ' . count(self::$summaryQuarantine));
Console::writeLine('Files whitelisted: ' . count(self::$summaryWhitelist));
Console::writeLine('Files ignored: ' . count(self::$summaryIgnored), 2);
}
Console::writeLine('Malware detected: ' . self::$summary_detected);
Console::writeLine('Malware detected: ' . self::$summaryDetected);
if (!self::$settings['report']) {
Console::writeLine('Malware removed: ' . count(self::$summary_removed));
Console::writeLine('Malware removed: ' . count(self::$summaryRemoved));
}

if (self::$settings['report']) {
Console::writeLine(Console::eol(1) . "Files infected: '" . self::$pathLogsInfected . "'", 1, 'red');
file_put_contents(self::$pathLogsInfected, 'Log date: ' . date('d-m-Y H:i:s') . Console::eol(1) . implode(Console::eol(2), self::$summary_ignored));
file_put_contents(self::$pathLogsInfected, 'Log date: ' . date('d-m-Y H:i:s') . Console::eol(1) . implode(Console::eol(2), self::$summaryIgnored));
Console::writeBreak(2);
} else {
if (count(self::$summary_removed) > 0) {
if (count(self::$summaryRemoved) > 0) {
Console::writeBreak();
Console::writeLine('Files removed:', 1, 'red');
foreach (self::$summary_removed as $un) {
foreach (self::$summaryRemoved as $un) {
Console::writeLine($un);
}
}
if (count(self::$summary_edited) > 0) {
if (count(self::$summaryEdited) > 0) {
Console::writeBreak();
Console::writeLine('Files edited:', 1, 'green');
foreach (self::$summary_edited as $un) {
foreach (self::$summaryEdited as $un) {
Console::writeLine($un);
}
}
if (count(self::$summary_quarantine) > 0) {
if (count(self::$summaryQuarantine) > 0) {
Console::writeBreak();
Console::writeLine('Files quarantined:', 1, 'yellow');
foreach (self::$summary_ignored as $un) {
foreach (self::$summaryIgnored as $un) {
Console::writeLine($un);
}
}
if (count(self::$summary_whitelist) > 0) {
if (count(self::$summaryWhitelist) > 0) {
Console::writeBreak();
Console::writeLine('Files whitelisted:', 1, 'cyan');
foreach (self::$summary_whitelist as $un) {
foreach (self::$summaryWhitelist as $un) {
Console::writeLine($un);
}
}
if (count(self::$summary_ignored) > 0) {
if (count(self::$summaryIgnored) > 0) {
Console::writeBreak();
Console::writeLine('Files ignored:', 1, 'cyan');
foreach (self::$summary_ignored as $un) {
foreach (self::$summaryIgnored as $un) {
Console::writeLine($un);
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/Console.php
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,11 @@ public static function helper()
-v --version - Get version number
--max-filesize="" - Set max filesize to scan (default: -1)
--ignore-path/s="" - Ignore path/s, for multiple value separate with comma
Wildcards are enabled ex. /path/*/cache or /path/*.log
--filter-path/s="" - Filter path/s, for multiple value separate with comma
Wildcards are enabled ex. /path/*/htdocs or /path/*.php
--exploits="" - Filter exploits
--functions="" - Define functions to search
--whitelist-only-path - Check on whitelist only file path and not line number
Expand All @@ -543,6 +547,7 @@ public static function helper()
php -d disable_functions='' scanner -s -logs="/user/marco/scanner.log"
php -d disable_functions='' scanner --agile --only-exploits
php -d disable_functions='' scanner --exploits="double_var2" --functions="eval, str_replace"
php -d disable_functions='' scanner --ignore-paths="/my/path/*.log,/my/path/*/cache/*"
EOD;
self::displayLine($help . self::eol(2) . Application::$argv->usage(), 2);
die();
Expand Down

0 comments on commit 3be18d3

Please sign in to comment.