Skip to content

Commit

Permalink
feat: improve whitelist system and some code improvement
Browse files Browse the repository at this point in the history
BREAKING CHANGE: csv whitelist doesn't work anymore
  • Loading branch information
marcocesarato committed Oct 6, 2020
1 parent cb6ac47 commit beeff01
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 223 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

scanner_infected.log
scanner.log
scanner_whitelist.csv
scanner_whitelist.json

vendor/**
.idea
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# AMWSCAN - PHP Antimalware Scanner

**Version:** 0.5.2.75 beta
**Version:** 0.5.3.93 beta

**Github:** https://github.com/marcocesarato/PHP-Antimalware-Scanner

Expand Down Expand Up @@ -129,7 +129,7 @@ When a malware is detected you will have the following choices (except when scan
- Dry run evil line code fixer `(fix code and confirm after a visual check)` [`--auto-clean-line`]
- Open with vim `(need php -d disable_functions='')`
- Open with nano `(need php -d disable_functions='')`
- Add to whitelist `(add to ./scanner_whitelist.csv)`
- Add to whitelist `(add to ./scanner-whitelist.json)`
- Show source
- Ignore [`--auto-skip`]

Expand Down
2 changes: 0 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
## TODO

* Output with EventSource + Offset mapping for continue scan
* Separate Actions from Application
* Whitelist with start and length
* Checksum files of the most popular platform for a whitelist (and implement the check with checksum) for no have more false positive
1. Wordpress
2. WooCommerce
Expand Down
Binary file modified dist/scanner
Binary file not shown.
2 changes: 1 addition & 1 deletion dist/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.2.75
0.5.3.93
151 changes: 151 additions & 0 deletions src/Actions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace marcocesarato\amwscan;

class Actions
{
/**
* Clean Evil Code.
*
* @param $code
* @param $pattern_found
*
* @return string|string[]|null
*/
public static function cleanEvilCode($code, $pattern_found)
{
foreach ($pattern_found as $pattern) {
preg_match('/(<\?php)(.*?)(' . preg_quote($pattern['match'], '/') . '[\s\r\n]*;?)/si', $code, $match);
$match[2] = trim($match[2]);
$match[4] = trim($match[4]);
if (!empty($match[2]) || !empty($match[4])) {
$code = str_replace($match[0], $match[1] . $match[2] . $match[4] . $match[5], $code);
} else {
$code = str_replace($match[0], '', $code);
}
$code = preg_replace('/<\?php[\s\r\n]*\?>/si', '', $code);
}

return $code;
}

/**
* Clean Evil Code Line.
*
* @param $code
* @param $pattern_found
*
* @return string
*/
public static function cleanEvilCodeLine($code, $pattern_found)
{
$lines = explode(PHP_EOL, $code);
foreach ($pattern_found as $pattern) {
unset($lines[(int)$pattern['line'] - 1]);
}
$code = implode(PHP_EOL, $lines);

return $code;
}

/**
* Delete File.
*
* @param $file
*
* @return bool
*/
public static function deleteFile($file)
{
return unlink($file);
}

/**
* Move to Quarantine.
*
* @param $file
*
* @return string
*/
public static function moveToQuarantine($file)
{
$quarantine = Application::$pathQuarantine . str_replace(realpath(Application::currentDirectory()), '', $file);
if (!is_dir(dirname($quarantine))) {
if (!mkdir($concurrentDirectory = dirname($quarantine), 0755, true) && !is_dir($concurrentDirectory)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
}
}
rename($file, $quarantine);

return $quarantine;
}

/**
* Add to Whitelist.
*
* @param $file
* @param $pattern_found
*
* @return false|int
*/
public static function addToWhitelist($file, $pattern_found)
{
foreach ($pattern_found as $key => $pattern) {
$exploit = $pattern['key'];
$lineNumber = $pattern['line'];
$match = $pattern['match'];
$fileName = str_replace(Application::$pathScan, '', $file);
$key = md5($exploit . $fileName . $lineNumber);
Application::$whitelist[$key] = array(
'file' => $fileName,
'exploit' => $exploit,
'line' => $lineNumber,
'match' => $match,
);
}

return file_put_contents(Application::$pathWhitelist, json_encode(Application::$whitelist));
}

/**
* Open with VIM.
*
* @param $file
*/
public static function openWithVim($file)
{
$descriptors = array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
$process = proc_open("vim '$file'", $descriptors, $pipes);
while (true) {
$proc_status = proc_get_status($process);
if ($proc_status['running'] == false) {
break;
}
}
}

/**
* Open with Nano.
*
* @param $file
*/
public static function openWithNano($file)
{
$descriptors = array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
$process = proc_open("nano '$file'", $descriptors, $pipes);
while (true) {
$proc_status = proc_get_status($process);
if ($proc_status['running'] == false) {
break;
}
}
}
}
101 changes: 26 additions & 75 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.2.75';
public static $version = '0.5.3.93';

/**
* Root path.
Expand Down Expand Up @@ -70,7 +70,7 @@ class Application
*
* @var string
*/
public static $pathWhitelist = '/scanner-whitelist.csv';
public static $pathWhitelist = '/scanner-whitelist.json';

/**
* Path to scan.
Expand Down Expand Up @@ -235,8 +235,14 @@ private function init()
self::$pathWhitelist = self::$root . self::$pathWhitelist;
self::$pathLogsInfected = self::$root . self::$pathLogsInfected;

// Prepare whitelist
self::$whitelist = CSV::read(self::$pathWhitelist);
// Load whitelist
if (file_exists(self::$pathWhitelist)) {
self::$whitelist = file_get_contents(self::$pathWhitelist);
self::$whitelist = @json_decode(self::$whitelist, true);
if (!is_array(self::$whitelist)) {
self::$whitelist = array();
}
}

Definitions::optimizeSig(Definitions::$SIGNATURES);
}
Expand Down Expand Up @@ -857,14 +863,12 @@ private function scan($iterator)
foreach ($pattern_found as $key => $pattern) {
$lineNumber = $pattern['line'];
$exploit = $pattern['key'];
$whitelist_filePath = trim($item[0], ' "');
$whitelist_exploit = trim($item[1], ' "');
$whitelist_lineNumber = trim($item[2], ' "');

// TODO: from char to length
if (strpos($_FILE_PATH, $whitelist_filePath) !== false &&
$exploit == $whitelist_exploit &&
(self::$settings['whitelist-only-path'] || (!self::$settings['whitelist-only-path'] && $lineNumber == $whitelist_lineNumber))) {
$match = $pattern['match'];

if (strpos($_FILE_PATH, $item['file']) !== false &&
$match === $item['match'] &&
$exploit === $item['exploit'] &&
(self::$settings['whitelist-only-path'] || (!self::$settings['whitelist-only-path'] && $lineNumber == $item['line']))) {
$in_whitelist++;
}
}
Expand Down Expand Up @@ -946,37 +950,20 @@ private function scan($iterator)
}
Console::newLine();
if ($confirm2 === 'y') {
unlink($_FILE_PATH);
Actions::deleteFile($_FILE_PATH);
self::$summaryRemoved[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' removed!", 2, 'green');
$_WHILE = false;
}
} elseif (in_array($confirmation, array('2', 'quarantine'))) {
// Move to quarantine
$quarantine = self::$pathQuarantine . str_replace(realpath(self::currentDirectory()), '', $_FILE_PATH);

if (!is_dir(dirname($quarantine))) {
if (!mkdir($concurrentDirectory = dirname($quarantine), 0755, true) && !is_dir($concurrentDirectory)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
}
}
rename($_FILE_PATH, $quarantine);
$quarantine = Actions::moveToQuarantine($_FILE_PATH);
self::$summaryQuarantine[] = $quarantine;
Console::writeLine("File '$_FILE_PATH' moved to quarantine!", 2, 'green');
$_WHILE = false;
} elseif (in_array($confirmation, array('3', 'clean')) && count($pattern_found) > 0) {
// Remove evil code
foreach ($pattern_found as $pattern) {
preg_match('/(<\?php)(.*?)(' . preg_quote($pattern['match'], '/') . '[\s\r\n]*;?)/si', $fc, $match);
$match[2] = trim($match[2]);
$match[4] = trim($match[4]);
if (!empty($match[2]) || !empty($match[4])) {
$fc = str_replace($match[0], $match[1] . $match[2] . $match[4] . $match[5], $fc);
} else {
$fc = str_replace($match[0], '', $fc);
}
$fc = preg_replace('/<\?php[\s\r\n]*\?\>/si', '', $fc);
}
$fc = Actions::cleanEvilCode($fc, $pattern_found);
Console::newLine();

$title = Console::title(' SANITIZED ', '=');
Expand Down Expand Up @@ -1004,11 +991,7 @@ private function scan($iterator)
}
} elseif (in_array($confirmation, array('4', 'clean-line')) && count($pattern_found) > 0) {
// Remove evil line code
$fc_expl = explode(PHP_EOL, $fc);
foreach ($pattern_found as $pattern) {
unset($fc_expl[(int)$pattern['line'] - 1]);
}
$fc = implode(PHP_EOL, $fc_expl);
$fc = Actions::cleanEvilCodeLine($fc, $pattern_found);

Console::newLine();

Expand Down Expand Up @@ -1036,52 +1019,20 @@ private function scan($iterator)
self::$summaryIgnored[] = $_FILE_PATH;
}
} elseif (in_array($confirmation, array('5', 'vim'))) {
// Edit with vim
$descriptors = array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
$process = proc_open("vim '$_FILE_PATH'", $descriptors, $pipes);
while (true) {
$proc_status = proc_get_status($process);
if ($proc_status['running'] == false) {
break;
}
}
// Open with vim
Actions::openWithVim($_FILE_PATH);
self::$summaryEdited[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' edited with vim!", 2, 'green');
self::$summaryRemoved[] = $_FILE_PATH;
} elseif (in_array($confirmation, array('6', 'nano'))) {
// Edit with nano
$descriptors = array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
$process = proc_open("nano -c '$_FILE_PATH'", $descriptors, $pipes);
while (true) {
$proc_status = proc_get_status($process);
if ($proc_status['running'] == false) {
break;
}
}
// Open with nano
Actions::openWithNano($_FILE_PATH);
self::$summaryEdited[] = $_FILE_PATH;
Console::writeLine("File '$_FILE_PATH' edited with nano!", 2, 'green');
self::$summaryRemoved[] = $_FILE_PATH;
} elseif (in_array($confirmation, array('7', 'whitelist'))) {
// Add to whitelist
foreach ($pattern_found as $key => $pattern) {
//$exploit = preg_replace("/^(\S+) \[line [0-9]+\].*/si", "$1", $key);
//$lineNumber = preg_replace("/^\S+ \[line ([0-9]+)\].*/si", "$1", $key);
$exploit = $pattern['key'];
$lineNumber = $pattern['line'];
self::$whitelist[] = array(str_replace(self::$pathScan, '', $_FILE_PATH), $exploit, $lineNumber);
}
self::$whitelist = array_map('unserialize', array_unique(array_map('serialize', self::$whitelist)));

// TODO: from char to length
if (CSV::write(self::$pathWhitelist, self::$whitelist)) {
if (Actions::addToWhitelist($_FILE_PATH, $pattern_found)) {
self::$summaryWhitelist[] = $_FILE_PATH;
Console::writeLine("Exploits of file '$_FILE_PATH' added to whitelist!", 2, 'green');
$_WHILE = false;
Expand All @@ -1104,7 +1055,7 @@ private function scan($iterator)
Console::display($title, 'white', 'red');
Console::newLine(2);
} else {
// None
// Skip
Console::writeLine("File '$_FILE_PATH' skipped!", 2, 'green');
self::$summaryIgnored[] = $_FILE_PATH;
$_WHILE = false;
Expand Down
Loading

0 comments on commit beeff01

Please sign in to comment.