Skip to content

Commit

Permalink
fix: multi exploit, functions and definitions detection
Browse files Browse the repository at this point in the history
Improve detection of exploit/functions and definitions and detecting
multiple denied functions on the same file scanned

Closes #14
  • Loading branch information
marcocesarato committed Dec 24, 2020
1 parent 75ac117 commit d1035a4
Showing 1 changed file with 72 additions and 36 deletions.
108 changes: 72 additions & 36 deletions src/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -783,53 +783,66 @@ public function scanFile($info)

if (0 === stripos($mime_type, 'text')) {
$deobfuctator = new Deobfuscator();
$contentRaw = file_get_contents($_FILE_PATH);
$contentClean = php_strip_whitespace($_FILE_PATH);
$contentDeobfuscated = $deobfuctator->deobfuscate($contentRaw);
$contentDecoded = $deobfuctator->decode($contentDeobfuscated);

$contents = array(
$contentRaw, // Original content
$contentClean, // Cleaned content
$contentDeobfuscated, // Deobfuscated content
$contentDecoded, // Decoded content
);

$fc = file_get_contents($_FILE_PATH);
$fc_clean = php_strip_whitespace($_FILE_PATH);
$fc_deobfuscated = $deobfuctator->deobfuscate($fc);
$fc_decoded = $deobfuctator->decode($fc_deobfuscated);

// Scan exploits
/**
* Scan exploits.
*/
$last_match = null;
foreach (self::$exploits as $key => $pattern) {
$match_description = null;
$lineNumber = null;
if (@preg_match($pattern, $fc, $match, PREG_OFFSET_CAPTURE) || // Original
@preg_match($pattern, $fc_clean, $match, PREG_OFFSET_CAPTURE) || // No comments
@preg_match($pattern, $fc_decoded, $match, PREG_OFFSET_CAPTURE)) { // Decoded
$last_match = $match[0][0];
$checkExploit = function ($match) use ($contentRaw, $pattern, $key, &$pattern_found) {
$match_description = null;
$lineNumber = null;
$last_match = $match[0];
$match_description = $key . "\n => " . $last_match;
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/i', $fc, $match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($fc, 0, $match[0][1])));
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/i', $contentRaw, $line_match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($contentRaw, 0, $line_match[0][1])));
$match_description = $key . ' [line ' . $lineNumber . "]\n => " . $last_match;
}
if (!empty($match_description)) {
//$pattern_found[$match_description] = $pattern;
$pattern_found[$match_description] = array(
'key' => $key,
'line' => $lineNumber,
'pattern' => $pattern,
'match' => $last_match,
);
}
};
// Check exploits
foreach ($contents as $content) {
if (@preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $match) {
$checkExploit($match);
}
}
}
}
unset($last_match, $match_description, $lineNumber, $match);

// Scan php commands
/**
* Scan functions.
*/
$last_match = null;
foreach (self::$functions as $_func) {
$match_description = null;
$func = preg_quote(trim($_func), '/');

// Basic search
$regex_pattern = "/(?:^|[\s\r\n]+|[^a-zA-Z0-9_>]+)(" . $func . "[\s\r\n]*\((?<=\().*?(?=\))\))/si";
if (@preg_match($regex_pattern, $fc_decoded, $match, PREG_OFFSET_CAPTURE) ||
@preg_match($regex_pattern, $fc_clean, $match, PREG_OFFSET_CAPTURE)) {
$last_match = explode($_func, $match[0][0]);
$checkFunction = function ($match) use ($contentRaw, $_func, $regex_pattern, &$pattern_found) {
$last_match = explode($_func, $match[0]);
$last_match = $_func . $last_match[1];
$match_description = $_func . "\n => " . $last_match;
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/', $fc, $match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($fc, 0, $match[0][1])));
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/', $contentRaw, $line_match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($contentRaw, 0, $line_match[0][1])));
$match_description = $_func . ' [line ' . $lineNumber . "]\n => " . $last_match;
}
if (!empty($match_description)) {
Expand All @@ -840,17 +853,25 @@ public function scanFile($info)
'match' => $last_match,
);
}
};
// Check functions
foreach ($contents as $content) {
if (@preg_match_all($regex_pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $match) {
$checkFunction($match);
}
}
}

// Check of base64
$regex_pattern_base64 = '/' . base64_encode($_func) . '/s';
if (@preg_match($regex_pattern_base64, $fc_decoded, $match, PREG_OFFSET_CAPTURE) ||
@preg_match($regex_pattern_base64, $fc_clean, $match, PREG_OFFSET_CAPTURE)) {
$last_match = explode($_func, $match[0][0]);
$checkBase64Function = function ($match) use ($contentRaw, $_func, $regex_pattern_base64, &$pattern_found) {
$last_match = explode($_func, $match[0]);
$last_match = $_func . $last_match[1];
$match_description = $_func . "_base64\n => " . $last_match;

if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/', $fc, $match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($fc, 0, $match[0][1])));
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/', $contentRaw, $line_match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($contentRaw, 0, $line_match[0][1])));
$match_description = $_func . '_base64 [line ' . $lineNumber . "]\n => " . $last_match;
}
if (!empty($match_description)) {
Expand All @@ -861,6 +882,14 @@ public function scanFile($info)
'match' => $last_match,
);
}
};
// Check base64 functions
foreach ($contents as $content) {
if (@preg_match_all($regex_pattern_base64, $content, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $match) {
$checkBase64Function($match);
}
}
}

/*$field = bin2hex($pattern);
Expand All @@ -872,16 +901,17 @@ public function scanFile($info)
$lineNumber = count(explode("\n", substr($fc, 0, $match[0][1])));
$pattern_found[$pattern . " [line " . $lineNumber . "]"] = $regex_pattern;
}*/

unset($last_match, $match_description, $lineNumber, $regex_pattern, $regex_pattern_base64, $match);
}

/**
* Check definitions.
*/
foreach (Definitions::$SIGNATURES as $key => $pattern) {
$regex_pattern = '#' . $pattern . '#smiS';
if (preg_match($regex_pattern, $fc_deobfuscated, $match, PREG_OFFSET_CAPTURE)) {
$last_match = $match[0][0];
if (!empty($last_match) && @preg_match('/' . preg_quote($match[0][0], '/') . '/', $fc, $match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($fc, 0, $match[0][1])));
$checkDefinitions = function ($match) use ($contentRaw, $key, $regex_pattern, &$pattern_found) {
$last_match = $match[0];
if (!empty($last_match) && @preg_match('/' . preg_quote($last_match, '/') . '/', $contentRaw, $line_match, PREG_OFFSET_CAPTURE)) {
$lineNumber = count(explode("\n", substr($contentRaw, 0, $line_match[0][1])));
$match_description = 'Sign ' . $key . ' [line ' . $lineNumber . "]\n => " . $last_match;
}
if (!empty($match_description)) {
Expand All @@ -892,10 +922,16 @@ public function scanFile($info)
'match' => $last_match,
);
}
};
// Check definitions
foreach ($contents as $content) {
if (@preg_match_all($regex_pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $match) {
$checkDefinitions($match);
}
}
}
}

unset($fc, $fc_decoded, $fc_clean, $fc_deobfuscated);
}

if ($is_favicon) {
Expand Down

0 comments on commit d1035a4

Please sign in to comment.