Nathanwoodburn.github.io/libraries/plugins/phpMussel/vault/functions.php
2021-08-30 15:06:36 +10:00

5510 lines
254 KiB
PHP

<?php
/**
* This file is a part of the phpMussel package.
* Homepage: https://phpmussel.github.io/
*
* PHPMUSSEL COPYRIGHT 2013 AND BEYOND BY THE PHPMUSSEL TEAM.
*
* Authors:
* @see PEOPLE.md
*
* License: GNU/GPLv2
* @see LICENSE.txt
*
* This file: Functions file (last modified: 2017.10.27).
*/
/**
* Extends compatibility with phpMussel to PHP 5.4.x by introducing some simple
* polyfills for functions introduced with newer versions of PHP.
*/
if (substr(PHP_VERSION, 0, 4) === '5.4.') {
require $phpMussel['Vault'] . 'php5.4.x.php';
}
/**
* Registers plugin closures/functions to their intended hooks.
*
* @param string $what The name of the closure/function to execute.
* @param string $where Where to execute it (the designated "plugin hook").
* @return bool Execution failed(false)/succeeded(true).
*/
$phpMussel['Register_Hook'] = function ($what, $where) use (&$phpMussel) {
if (
!isset($phpMussel['MusselPlugins']['hooks']) ||
!isset($phpMussel['MusselPlugins']['closures']) ||
!$what ||
!$where
) {
return false;
}
if (!isset($phpMussel['MusselPlugins']['hooks'][$where])) {
$phpMussel['MusselPlugins']['hooks'][$where] = [];
}
$phpMussel['MusselPlugins']['hooks'][$where][] = $what;
if (!function_exists($what) && isset($GLOBALS[$what]) && is_object($GLOBALS[$what])) {
$phpMussel['MusselPlugins']['closures'][] = $what;
}
return true;
};
/**
* Executes plugin closures/functions.
*
* @param string $HookID Where to execute it (the designated "plugin hook").
* @return bool Execution failed(false)/succeeded(true).
*/
$phpMussel['Execute_Hook'] = function ($HookID) use (&$phpMussel) {
if (!isset($phpMussel['MusselPlugins']['hooks'][$HookID])) {
return false;
}
foreach ($phpMussel['MusselPlugins']['hooks'][$HookID] as $Registered) {
if (isset($GLOBALS[$Registered]) && is_object($GLOBALS[$Registered])) {
$GLOBALS[$Registered]();
} elseif (function_exists($Registered)) {
call_user_func($Registered);
}
}
return true;
};
/**
* Replaces encapsulated substrings within an input string with the value of
* elements within an input array, whose keys correspond to the substrings.
* Accepts two input parameters: An input array (1), and an input string (2).
*
* @param array $Needle The input array (the needle[/s]).
* @param string $Haystack The input string (the haystack).
* @return string The resultant string.
*/
$phpMussel['ParseVars'] = function ($Needle, $Haystack) {
if (!is_array($Needle) || empty($Haystack)) {
return '';
}
array_walk($Needle, function($Value, $Key) use (&$Haystack) {
if (!is_array($Value)) {
$Haystack = str_replace('{' . $Key . '}', $Value, $Haystack);
}
});
return $Haystack;
};
/**
* Implodes multidimensional arrays.
*
* @param array $ar The array to be imploded.
* @param string|array $j An optional "needle" or "joiner" to use for imploding
* the array. If a numeric array is used, an element of the array
* corresponding to the recursion depth will be used as the needle or
* joiner.
* @param int $i Used by the function when calling itself recursively, for the
* purpose of tracking recursion depth (shouldn't be used outside the
* function).
* @param bool $e Optional; When set to false, empty elements will be ignored.
* @return string The imploded array.
*/
$phpMussel['implode_md'] = function ($ar, $j = '', $i = 0, $e = true) use (&$phpMussel) {
if (!is_array($ar)) {
return $ar;
}
$c = count($ar);
if (!$c || is_array($i)) {
return false;
}
if (is_array($j)) {
if (!$x = $j[$i]) {
return false;
}
} else {
$x = $j;
}
$out = '';
while ($c > 0) {
$key = key($ar);
if (is_array($ar[$key])) {
$i++;
$ar[$key] = $phpMussel['implode_md']($ar[$key], $j, $i);
$i--;
}
if (!$out) {
$out = $ar[$key];
} elseif (!(!$e && empty($ar[$key]))) {
$out .= $x . $ar[$key];
}
next($ar);
$c--;
}
return $out;
};
/**
* A function for comparing substrings with hex values.
*
* @param string $str The source/raw string to be compared.
* @param int $st Optional; At what point in the source/raw string should the
* comparing begin? The first character of the string starts at "0".
* @param int $l Optional; For how many bytes of the string, starting from
* `$st`, should the comparing occur? Defaults to the total length of the
* string.
* @param string $x The hexadecimal value to compare the string against.
* @param bool $p Optional; When set to false, the function will return true if
* the hexadecimal value can be matched to at least some part of the
* substring being compared; When set to true, the function will return
* true if the hexadecimal value is an exact match to the entirety of the
* substring being compared (optional).
* @return bool The results of the comparison (true if matched, false if not
* matched).
*/
$phpMussel['substr_compare_hex'] = function ($str = '', $st = 0, $l = 0, $x = 0, $p = false) {
if (!$l) {
$l = strlen($str);
}
if (!$x || !$l) {
return false;
}
for ($str = substr($str, $st, $l), $y = '', $i = 0; $i < $l; $i++) {
$z = dechex(ord(substr($str, $i, 1)));
$y .= (strlen($z) === 1) ? $z = '0' . $z : $z;
}
return !$p ? (substr_count($y, strtolower($x)) > 0) : ($y === strtolower($x));
};
/**
* Does some simple decoding work on strings.
*
* @param string $str The string to be decoded.
* @return string The decoded string.
*/
$phpMussel['prescan_decode'] = function ($str) use (&$phpMussel) {
$nstr = html_entity_decode(urldecode(str_ireplace('&amp;#', '&#', str_ireplace('&amp;amp;', '&amp;', $str))));
if ($nstr !== $str) {
$nstr = $phpMussel['prescan_decode']($nstr);
}
return $nstr;
};
/**
* Some simple obfuscation for potentially blocked functions; We need this to
* avoid triggering false positives for some potentially overzealous
* server-based security solutions that would usually flag this file as
* malicious when they detect it containing the names of suspect functions.
*
* @param string $n An alias for the function that we want to call.
* @param string $str Some data to parse to the function being called.
* @return string The parsed data and/or decoded string (if $str is empty, the
* the resolved alias will be returned instead).
*/
$phpMussel['Function'] = function ($n, $str = '') {
static $x = 'abcdefghilnorstxz12346_';
$fList = [
'GZ' =>
$x[6] . $x[16] . $x[8] . $x[10] . $x[5] . $x[9] . $x[0] . $x[14] . $x[4],
'R13' =>
$x[13] . $x[14] . $x[12] . $x[22] . $x[12] . $x[11] . $x[14] . $x[17] . $x[19],
'B64' =>
$x[1] . $x[0] . $x[13] . $x[4] . $x[21] . $x[20] . $x[22] . $x[3] . $x[4] . $x[2] . $x[11] . $x[3] . $x[4],
'HEX' =>
$x[7] . $x[4] . $x[15] . $x[18] . $x[1] . $x[8] . $x[10]
];
if (!isset($fList[$n])) {
return '';
}
if (!$str || !function_exists($fList[$n])) {
return $fList[$n];
}
try {
$Return = $fList[$n]($str);
} catch (\Exception $e) {
$Return = '';
}
return $Return;
};
/**
* Does some more complex decoding and normalisation work on strings.
*
* @param string $str The string to be decoded/normalised.
* @param bool $html If true, "style" and "script" tags will be stripped from
* the input string (optional; defaults to false).
* @param bool $decode If false, the input string will be normalised, but not
* decoded; If true, the input string will be normalised *and* decoded.
* Optional; Defaults to false.
* @return string The decoded/normalised string.
*/
$phpMussel['prescan_normalise'] = function ($str, $html = false, $decode = false) use (&$phpMussel) {
$ostr = '';
if ($decode) {
$ostr .= $str;
while (true) {
if (function_exists($phpMussel['Function']('GZ'))) {
if ($c = preg_match_all(
'/(' . $phpMussel['Function']('GZ') . '\s*\(\s*["\'])(.{1,4096})(,[0-9])?(["\']\s*\))/i',
$str, $matches)) {
for ($i = 0; $c > $i; $i++) {
$str = str_ireplace(
$matches[0][$i],
'"' . $phpMussel['Function']('GZ', $phpMussel['substrbl']($phpMussel['substraf']($matches[0][$i], $matches[1][$i]), $matches[4][$i])) . '"',
$str
);
}
continue;
}
}
if ($c = preg_match_all(
'/(' . $phpMussel['Function']('B64') . '|decode_base64|base64\.b64decode|atob|Base64\.decode64)(\s*' .
'\(\s*["\'\`])([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)(["\'\`]' .
'\s*\))/i',
$str, $matches)) {
for ($i = 0; $c > $i; $i++) {
$str = str_ireplace(
$matches[0][$i],
'"' . $phpMussel['Function']('B64', $phpMussel['substrbl']($phpMussel['substraf']($matches[0][$i], $matches[1][$i] . $matches[2][$i]), $matches[5][$i])) . '"',
$str
);
}
continue;
}
if ($c = preg_match_all(
'/(' . $phpMussel['Function']('R13') . '\s*\(\s*["\'])([^\'"\(\)]{1,4096})(["\']\s*\))/i',
$str, $matches)) {
for ($i = 0; $c > $i; $i++) {
$str = str_ireplace(
$matches[0][$i],
'"' . $phpMussel['Function']('R13', $phpMussel['substrbl']($phpMussel['substraf']($matches[0][$i], $matches[1][$i]), $matches[3][$i])) . '"',
$str
);
}
continue;
}
if ($c = preg_match_all(
'/(' . $phpMussel['Function']('HEX') . '\s*\(\s*["\'])([a-fA-F0-9]{1,4096})(["\']\s*\))/i',
$str, $matches )) {
for ($i = 0; $c > $i; $i++) {
$str = str_ireplace(
$matches[0][$i],
'"' . $phpMussel['HexSafe']($phpMussel['substrbl']($phpMussel['substraf']($matches[0][$i], $matches[1][$i]), $matches[3][$i])) . '"',
$str
);
}
continue;
}
if ($c = preg_match_all(
'/([Uu][Nn][Pp][Aa][Cc][Kk]\s*\(\s*["\']\s*H\*\s*["\']\s*,\s*["\'])([a-fA-F0-9]{1,4096})(["\']\s*\))/',
$str, $matches)) {
for ($i = 0; $c > $i; $i++) {
$str = str_replace($matches[0][$i], '"' . $phpMussel['HexSafe']($phpMussel['substrbl']($phpMussel['substraf']($matches[0][$i], $matches[1][$i]), $matches[3][$i])) . '"', $str);
}
continue;
}
break;
}
}
$str = preg_replace('/[^\x21-\x7e]/', '', strtolower($phpMussel['prescan_decode']($str . $ostr)));
unset($ostr);
if ($html) {
$str = preg_replace([
'@<script[^>]*?>.*?</script>@si',
'@<[\/\!]*?[^<>]*?>@si',
'@<style[^>]*?>.*?</style>@siU',
'@<![\s\S]*?--[ \t\n\r]*>@'
], '', $str);
}
return trim($str);
};
/**
* Gets substring from haystack prior to the first occurrence of needle.
*
* @param string $h The haystack.
* @param string $n The needle.
* @return string The substring.
*/
$phpMussel['substrbf'] = function ($h, $n) {
return !$n ? '' : substr($h, 0, strpos($h, $n));
};
/**
* Gets substring from haystack after the first occurrence of needle.
*
* @param string $h The haystack.
* @param string $n The needle.
* @return string The substring.
*/
$phpMussel['substraf'] = function ($h, $n) {
return !$n ? '' : substr($h, strpos($h, $n) + strlen($n));
};
/**
* Gets substring from haystack prior to the last occurrence of needle.
*
* @param string $h The haystack.
* @param string $n The needle.
* @return string The substring.
*/
$phpMussel['substrbl'] = function ($h, $n) {
return !$n ? '' : substr($h, 0, strrpos($h, $n));
};
/**
* Gets substring from haystack after the last occurrence of needle.
*
* @param string $h The haystack.
* @param string $n The needle.
* @return string The substring.
*/
$phpMussel['substral'] = function ($h, $n) {
return !$n ? '' : substr($h, strrpos($h, $n) + strlen($n));
};
/**
* This function reads files and returns the contents of those files.
*
* @param string $File Path and filename of the file to read.
* @param int $s Number of blocks to read from the file (optional; can be
* manually specified, but it's best to just ignore it and let the
* function work it out for itself).
* @param bool $PreChecked When false, checks that the file exists and is
* writable. Defaults to false.
* @param int $Blocks The total size of a single block in kilobytes (optional;
* defaults to 128, i.e., 128KB or 131072 bytes). This can be modified by
* developers as per their individual needs. Generally, a smaller value
* will increase stability but decrease performance, whereas a larger
* value will increase performance but decrease stability.
* @return string|bool Content of the file returned by the function (or false
* on failure).
*/
$phpMussel['ReadFile'] = function ($File, $Size = 0, $PreChecked = false, $Blocks = 128) {
if (!$PreChecked && (!is_file($File) || !is_readable($File))) {
return false;
}
$Blocksize = $Blocks * 1024;
$Filesize = filesize($File);
if (!$Size) {
$Size = ($Filesize && $Blocksize) ? ceil($Filesize / $Blocksize) : 0;
}
$Data = '';
if ($Size > 0) {
$Handle = fopen($File, 'rb');
$r = 0;
while ($r < $Size) {
$Data .= fread($Handle, $Blocksize);
$r++;
}
fclose($Handle);
}
return $Data ?: false;
};
/**
* A very simple wrapper for file() that checks for the existence of files
* before attempting to read them, in order to avoid warnings about
* non-existent files.
*
* @param string $Filename Refer to the description for file().
* @param int $Flags Refer to the description for file().
* @param array $Context Refer to the description for file().
* @return array|bool Same as with file(), but won't trigger warnings.
*/
$phpMussel['ReadFileAsArray'] = function ($Filename, $Flags = 0, $Context = false) {
if (!is_readable($Filename)) {
return false;
}
if (!$Context) {
return !$Flags ? file($Filename) : file($Filename, $Flags);
}
return file($Filename, $Flags, $Context);
};
/** Deletes expired cache entries and regenerates cache files. */
$phpMussel['CleanCache'] = function () use (&$phpMussel) {
if (!empty($phpMussel['memCache']['CacheCleaned'])) {
return true;
}
$phpMussel['memCache']['CacheCleaned'] = true;
$CacheFiles = [];
$FileIndex = $phpMussel['Vault'] . 'cache/index.dat';
if (!is_readable($FileIndex)) {
return false;
}
$FileDataOld = $FileData = $phpMussel['ReadFile']($FileIndex);
if (substr_count($FileData, ';')) {
$FileData = explode(';', $FileData);
foreach ($FileData as &$ThisData) {
if (strpos($ThisData, ':') === false) {
$ThisData = '';
continue;
}
$ThisData = explode(':', $ThisData, 3);
if ($ThisData[1] > 0 && $phpMussel['Time'] > $ThisData[1]) {
$FileKey = bin2hex(substr($ThisData[0], 0, 1));
if (!isset($CacheFiles[$FileKey])) {
$CacheFiles[$FileKey] = !is_readable(
$phpMussel['cachePath'] . $FileKey . '.tmp'
) ? '' : $phpMussel['ReadFile']($phpMussel['cachePath'] . $FileKey . '.tmp', 0, true);
}
while (strpos($CacheFiles[$FileKey], $ThisData[0] . ':') !== false) {
$CacheFiles[$FileKey] = str_ireplace($ThisData[0] . ':' . $phpMussel['substrbf'](
$phpMussel['substraf']($CacheFiles[$FileKey], $ThisData[0] . ':'), ';'
) . ';', '', $CacheFiles[$FileKey]);
}
$ThisData = '';
continue;
}
$ThisData = $ThisData[0] . ':' . $ThisData[1];
}
$FileData = str_replace(';;', ';', implode(';', array_filter($FileData)) . ';');
if ($FileDataOld !== $FileData) {
$Handle = fopen($FileIndex, 'w');
fwrite($Handle, $FileData);
fclose($Handle);
}
}
foreach ($CacheFiles as $CacheEntryKey => $CacheEntryValue) {
if (strlen($CacheEntryValue) < 2) {
if (file_exists($phpMussel['cachePath'] . $CacheEntryKey . '.tmp')) {
unlink($phpMussel['cachePath'] . $CacheEntryKey . '.tmp');
}
continue;
}
$Handle = fopen($phpMussel['cachePath'] . $CacheEntryKey . '.tmp', 'w');
fwrite($Handle, $CacheEntryValue);
fclose($Handle);
}
return true;
};
/**
* Retrieves cache entries.
*
* @param string|array $Entry The name of the cache entry/entries to retrieve;
* Can be a string to specify a single entry, or an array of strings to
* specify multiple entries.
* @return string|array Contents of the cache entry/entries.
*/
$phpMussel['FetchCache'] = function ($Entry = '') use (&$phpMussel) {
$phpMussel['CleanCache']();
if (!$Entry) {
return '';
}
if (is_array($Entry)) {
$Out = [];
array_walk($Entry, function($Value, $Key) use (&$phpMussel, &$Out) {
$Out[$Key] = $phpMussel['FetchCache']($Value);
});
return $Out;
}
$File = $phpMussel['cachePath'] . bin2hex(substr($Entry, 0, 1)) . '.tmp';
if (!is_readable($File) || !$FileData = $phpMussel['ReadFile']($File, 0, true)) {
return '';
}
if (!$Item = strpos($FileData, $Entry . ':') !== false ? $Entry . ':' . $phpMussel['substrbf'](
$phpMussel['substraf']($FileData, $Entry . ':'), ';'
) . ';' : '') {
return '';
}
$Expiry = $phpMussel['substrbf']($phpMussel['substraf']($Item, $Entry . ':'), ':');
if ($Expiry > 0 && $phpMussel['Time'] > $Expiry) {
while (substr_count($FileData, $Entry . ':')) {
$FileData = str_ireplace($Item, '', $FileData);
}
$Handle = fopen($File, 'w');
fwrite($Handle, $FileData);
fclose($Handle);
return '';
}
if (!$ItemData = $phpMussel['substrbf']($phpMussel['substraf']($Item, $Entry . ':' . $Expiry . ':'), ';')) {
return '';
}
return $phpMussel['Function']('GZ', $phpMussel['HexSafe']($ItemData)) ?: '';
};
/**
* Creates cache entry and saves it to the cache.
*
* @param string $Entry Name of the cache entry to create.
* @param int $Expiry Unix time until the cache entry expires.
* @param string $ItemData Contents of the cache entry.
* @return bool This should always return true, unless something goes wrong.
*/
$phpMussel['SaveCache'] = function ($Entry = '', $Expiry = 0, $ItemData = '') use (&$phpMussel) {
$phpMussel['CleanCache']();
if (!$Entry || !$ItemData) {
return false;
}
if (!$Expiry) {
$Expiry = $phpMussel['Time'];
}
$File = $phpMussel['cachePath'] . bin2hex($Entry[0]) . '.tmp';
$Data = $phpMussel['ReadFile']($File) ?: '';
while (substr_count($Data, $Entry . ':')) {
$Data = str_ireplace($Entry . ':' . $phpMussel['substrbf']($phpMussel['substraf']($Data, $Entry . ':'), ';') . ';', '', $Data);
}
$Data .= $Entry . ':' . $Expiry . ':' . bin2hex(gzdeflate($ItemData,9)) . ';';
$Handle = fopen($File, 'w');
fwrite($Handle, $Data);
fclose($Handle);
$IndexFile = $phpMussel['Vault'] . 'cache/index.dat';
$IndexNewData = $IndexData = $phpMussel['ReadFile']($IndexFile) ?: '';
while (substr_count($IndexNewData, $Entry . ':')) {
$IndexNewData = str_ireplace($Entry . ':' . $phpMussel['substrbf']($phpMussel['substraf']($IndexNewData, $Entry . ':'), ';') . ';', '', $IndexNewData);
}
$IndexNewData .= $Entry . ':' . $Expiry . ';';
if ($IndexNewData !== $IndexData) {
$IndexHandle = fopen($IndexFile, 'w');
fwrite($IndexHandle, $IndexNewData);
fclose($IndexHandle);
}
return true;
};
/** Reads and prepares cached hash data. */
$phpMussel['PrepareHashCache'] = function () use (&$phpMussel) {
$phpMussel['HashCache']['Data'] =
$phpMussel['Config']['general']['scan_cache_expiry'] > 0 ? $phpMussel['FetchCache']('HashCache') : '';
if (!empty($phpMussel['HashCache']['Data'])) {
$phpMussel['HashCache']['Data'] = explode(';', $phpMussel['HashCache']['Data']);
$Build = [];
foreach ($phpMussel['HashCache']['Data'] as $CacheItem) {
if (strpos($CacheItem, ':') !== false) {
$CacheItem = explode(':', $CacheItem, 4);
if (!($phpMussel['Time'] > $CacheItem[1])) {
$Build[$CacheItem[0]] = $CacheItem;
}
}
}
$phpMussel['HashCache']['Data'] = $Build;
}
};
/**
* Quarantines file uploads by using a key generated from your quarantine key
* to bitshift the input string (the file uploads), appending a header with an
* explanation of what the bitshifted data is, along with an MD5 hash checksum
* of its non-quarantined counterpart, and then saves it all to a QFU file,
* storing these QFU files in your quarantine directory.
*
* This isn't hardcore encryption, but it should be sufficient to prevent
* accidental execution of quarantined files and to allow safe handling of
* those files, which is the whole point of quarantining them in the first
* place. Improvements might be made in the future.
*
* @param string $In The input string (the file upload / source data).
* @param string $key Your quarantine key.
* @param string $ip Data origin (usually, the IP address of the uploader).
* @param string $id The QFU filename to use (calculated beforehand).
* @return bool This should always return true, unless something goes wrong.
*/
$phpMussel['Quarantine'] = function ($In, $key, $ip, $id) use (&$phpMussel) {
if (!$In || !$key || !$ip || !$id || !function_exists('gzdeflate') || (
strlen($key) < 128 &&
!$key = $phpMussel['HexSafe'](hash('sha512', $key) . hash('whirlpool', $key))
)) {
return false;
}
$k = strlen($key);
$FileSize = strlen($In);
$h = "\xa1phpMussel\x21" . $phpMussel['HexSafe'](md5($In)) . pack('l*', $FileSize) . "\x01";
$In = gzdeflate($In, 9);
$Out = '';
$i = 0;
while ($i < $FileSize) {
for ($j = 0; $j < $k; $j++, $i++) {
if (strlen($Out) >= $FileSize) {
break 2;
}
$L = substr($In, $i, 1);
$R = substr($key, $j, 1);
$Out .= ($L === false ? "\x00" : $L) ^ ($R === false ? "\x00" : $R);
}
}
$Out =
"\x2f\x3d\x3d\x20phpMussel\x20Quarantined\x20File\x20Upload\x20\x3d" .
"\x3d\x5c\n\x7c\x20Time\x2fDate\x20Uploaded\x3a\x20" .
str_pad($phpMussel['Time'], 18, "\x20") .
"\x7c\n\x7c\x20Uploaded\x20From\x3a\x20" . str_pad($ip, 22, "\x20") .
"\x20\x7c\n\x5c" . str_repeat("\x3d", 39) . "\x2f\n\n\n" . $h . $Out;
$u = $phpMussel['MemoryUse']($phpMussel['qfuPath']);
$u = $u['s'] + strlen($Out);
if ($u > $phpMussel['ReadBytes']($phpMussel['Config']['general']['quarantine_max_usage'])) {
$u = $phpMussel['MemoryUse'](
$phpMussel['qfuPath'],
$u - $phpMussel['ReadBytes']($phpMussel['Config']['general']['quarantine_max_usage'])
);
}
$Handle = fopen($phpMussel['qfuPath'] . $id . '.qfu', 'a');
fwrite($Handle, $Out);
fclose($Handle);
if (!$phpMussel['EOF']) {
$phpMussel['Stats-Increment']('Web-Quarantined', 1);
}
return true;
};
/**
* Calculates the memory usage of a directory, and optionally, enforces a
* limitation upon the memory usage of that directory by way of deleting the
* contents of that directory until a specified quota of bytes to be deleted
* has been met.
*
* This function is recursive, and will check (and/or delete from) the
* specified directory and all subdirectories that it contains; It should be
* regarded as a subfunction of the quarantine functionality, used by the
* quarantine functionality to enforce the quarantine memory usage limit.
*
* @param string $p The path and name of the directory to be checked.
* @param int $d A quota for how many bytes should be deleted from the target
* directory when the function is executed (omitting this parameter, or
* setting it to zero or less, will prevent the deletion of any files).
* @return array The function will return an array containing four elements,
* all integers: `s` is the actual total memory usage of the target
* directory, `c` is a count of the total number of objects (files and
* subdirectories) detected within the target directory, `dc` is a count
* only of the total number of subdirectories detected within the target
* directory, and `d` is how much remaining quota there is to be met by
* the time the function has finished executing (usually, should be zero
* or less).
*/
$phpMussel['MemoryUse'] = function ($p, $d = 0) use (&$phpMussel) {
$t = ['s' => 0, 'c' => 0, 'dc' => 0, 'd' => $d];
if (is_dir($p) && is_readable($p) && $h = opendir($p)) {
while (false !== ($f = readdir($h))) {
if ($f !== '.' && $f !== '..' && !is_link($np = $p . '/' . $f)) {
if (is_dir($np)) {
$t['dc']++;
$r = $phpMussel['MemoryUse']($np, $t['d']);
$t['s'] += $r['s'];
$t['c'] += $r['c'];
$t['dc'] += $r['dc'];
$t['d'] -= $r['d'];
} elseif (is_file($np)) {
$ns = filesize($np);
if ($t['d'] > 0 && substr_count($np . "\x01", ".qfu\x01") > 0 && is_readable($np)) {
unlink($np);
$t['d'] -= $ns;
} else {
$t['s'] += $ns;
$t['c']++;
}
}
}
}
closedir($h);
}
return $t;
};
/**
* Checks if $needle (string) matches (is equal or identical to) $haystack
* (string), or a specific substring of $haystack, to within a specific
* threshold of the levenshtein distance between the $needle and the $haystack
* or the $haystack substring specified.
*
* This function is useful for expressing the differences between two strings
* as an integer value and for then determining whether a specific value as per
* those differences is met.
*
* @param string $needle The needle (will be matched against the $haystack, or,
* if substring positions are specified, against the $haystack substring
* specified).
* @param string $haystack The haystack (will be matched against the $needle).
* Note that for the purposes of calculating the levenshtein distance, it
* doesn't matter which string is a $needle and which is a $haystack (the
* value should be the same if the two were reversed). However, when
* specifying substring positions, those substring positions are applied
* to the $haystack, and not the $needle. Note, too, that if the $needle
* length is greater than the $haystack length (after having applied the
* substring positions to the $haystack), $needle and $haystack will be
* switched.
* @param int $pos_A The initial position of the $haystack to use for the
* substring, if using a substring (optional; defaults to `0`; `0` is the
* beginning of the $haystack).
* @param int $pos_Z The final position of the $haystack to use for the
* substring, if using a substring (optional; defaults to `0`; `0` will
* instruct the function to continue to the end of the $haystack, and
* thus, if both $pos_A and $pos_Z are `0`, the entire $haystack will be
* used).
* @param int $min The threshold minimum (the minimum levenshtein distance
* required in order for the two strings to be considered a match).
* Optional; Defaults to `0`. If `0` or less is specified, there is no
* minimum, and so, any and all strings should always match, as long as
* the levenshtein distance doesn't surpass the threshold maximum.
* @param int $max The threshold maximum (the maximum levenshtein distance
* allowed for the two strings to be considered a match). Optional;
* Defaults to `-1`. If exactly `-1` is specified, there is no maximum,
* and so, any and all strings should always match, as long as the
* threshold minimum is met.
* @param bool $bool Specifies to the function whether to return the
* levenshtein distance of the two strings (as an integer) or to return
* the results of the match (as a boolean; true for match success, false
* for match failure). Optional; Defaults to true. If true is specified,
* the function will return a boolean value (the results of the match),
* and if false is specified, the levenshtein distance will be returned.
* @param bool $case Specifies to the function whether to treat the two strings
* as case-sensitive (when true is specified) or case-insensitive (when
* false is specified) when calculating the levenshtein distance.
* Optional; Defaults to false.
* @param int $cost_ins The cost to apply for character/byte insertions for
* when calculating the levenshtein distance. Optional; Defaults to 1.
* @param int $cost_rep The cost to apply for character/byte replacements for
* when calculating the levenshtein distance. Optional; Defaults to 1.
* @param int $cost_del The cost to apply for character/byte deletions for when
* calculating the levenshtein distance. Optional; Defaults to 1.
* @return bool|int The function will return either a boolean or an integer,
* depending on the state of $bool (but will also return false whenever an
* error occurs).
*/
$phpMussel['lv_match'] = function ($needle, $haystack, $pos_A = 0, $pos_Z = 0, $min = 0, $max = -1, $bool = true, $case = false, $cost_ins = 1, $cost_rep = 1, $cost_del = 1) {
if (!function_exists('levenshtein') || is_array($needle) || is_array($haystack)) {
return false;
}
$nlen = strlen($needle);
$pos_A = (int)$pos_A;
$pos_Z = (int)$pos_Z;
$min = (int)$min;
$max = (int)$max;
if ($pos_A !== 0 || $pos_Z !== 0) {
$haystack =
($pos_Z === 0) ?
substr($haystack, $pos_A) :
substr($haystack, $pos_A, $pos_Z);
}
$hlen = strlen($haystack);
if ($nlen < 1 || $hlen < 1) {
return $bool ? false : 0;
}
if ($nlen > $hlen) {
$x = [$needle, $nlen, $haystack, $hlen];
$haystack = $x[0];
$hlen = $x[1];
$needle = $x[2];
$nlen = $x[3];
}
if ($cost_ins === 1 && $cost_rep === 1 && $cost_del === 1) {
$lv = $case ? levenshtein(
$haystack, $needle
) : levenshtein(
strtolower($haystack), strtolower($needle)
);
} else {
$lv = $case ? levenshtein(
$haystack, $needle, $cost_ins, $cost_rep, $cost_del
) : levenshtein(
strtolower($haystack), strtolower($needle), $cost_ins, $cost_rep, $cost_del
);
}
return $bool ? (($min === 0 || $lv >= $min) && ($max === -1 || $lv <= $max)) : $lv;
};
/**
* Returns the high and low nibbles corresponding to the first byte of the
* input string.
*
* @param string $n The input string.
* @return array Contains two elements, both standard decimal integers; The
* first is the high nibble of the input string, and the second is the low
* nibble of the input string.
*/
$phpMussel['split_nibble'] = function ($n) {
$n = bin2hex($n);
return [hexdec(substr($n, 0, 1)), hexdec(substr($n, 1, 1))];
};
/**
* Constructs an output string representing the binary bits of an input string,
* whereby each byte of the output string is a digit (1 or 0), representing
* the 1/ON or 0/OFF bits of the input string respectively.
* `$phpMussel['explode_bits']()` can be reversed by way of using
* `$phpMussel['implode_bits']()`.
*
* @param string $n The input string (see closure description above).
* @return string $n The output string (see closure description above).
*/
$phpMussel['explode_bits'] = function ($n) {
$out = '';
$len = strlen($n);
for ($i = 0; $i < $len; $i++) {
$out .= str_pad(decbin(ord($n[$i])), 8, '0', STR_PAD_LEFT);
}
return $out;
};
/**
* Reconstitutes an output string from an input string representing a series of
* binary bits. Each byte is a digit (1 or 0), representing 1/ON or 0/OFF bits
* of the output string respectively. `$phpMussel['implode_bits']()` can be
* used to reverse strings generated by `$phpMussel['explode_bits']()`.
*
* @param string $n The input string (see closure description above).
* @return string $n The output string (see closure description above).
*/
$phpMussel['implode_bits'] = function ($n) {
$chars_chunks = str_split($n, 8);
$num = count($chars_chunks);
for ($out = '', $i = 0; $i < $num; $i++) {
$out .= chr(bindec($chars_chunks[$i]));
}
return $out;
};
/**
* Translates the virus name/identifier shorthand adopted by phpMussel to
* proper virus names/identifiers and makes some determinations regarding
* detections based upon the interpretation and understanding of that shorthand
* against the phpMussel configuration (for example, whether some certain
* classifications, such as hoax/joke viruses, adware, etc, should be entirely
* ignored by the scanner, or should be identified as malicious, and therefore
* blocked).
*
* Originally, this function was created to allow a way for phpMussel to
* partially compress its signatures without jeopardising speed, performance or
* processing efficiency, because, by allowing phpMussel to partially compress
* its signatures, the total footprint of its signature files could be reduced,
* therefore allowing the inclusion of a greater number of signatures in the
* signature files without causing an excessive bloat in the total footprint.
*
* However, since then, its purpose has expanded, to determining whether a
* signature should be considered a weighted signature (for more complex
* detections involving multiple signatures) or a non-weighted signature (for
* signatures that are detections in their own right, not requiring additional
* signatures for the detection to occur), and to determining whether or not a
* signature should be ignored, based upon its classification.
*
* The function takes an input string (the shorthand virus name), and if byte 0
* of the input string is "\x1A" (the substitute character), the input string
* is a shorthand virus name, and processing continues; If it doesn't begin
* with "\x1A", it isn't a shorthand virus name, and the input string should be
* returned to the calling scope unmodified. When processing continues, the
* function splits the nibbles of bytes 1-2, and uses that information to
* reconstruct a complete virus name from the shorthand virus name; 1H
* represents the signature vendor name and 1L optionally provides some
* additional generic indicators (heuristic, CVE, etc), except when 1H == 8 (in
* which case, 1L represents the signature vendor name, 1H == 8 being used to
* access that additional set of allocations), 2H+2L represents the virus
* target (i.e., the file format or system that the virus that the signature is
* intended to detect is intended to be targeting), and 3H+3L represents the
* nature of what the signature is intended to detect (i.e., whether we should
* call it a virus, a trojan, adware, ransomware, etc).
*
* Warning: When modifying these allocations (such as to include a new vendor
* or a new type of detection), be very careful to ensure that your choice of
* allocations won't conflict with the what phpMussel recognises as its
* delimiters or as special characters (newlines, semicolons, colons, etc), or
* else your signature files could break very badly, leading either to a
* failure to properly detect anything or to numerous severe false positives.
* Generally (but not exclusively), you should avoid: "\x0?" (or any ?H0
* character/byte), \x3A and \x3B. Also avoid using the null character ("\x00")
* in particular, because it can severely break things in certain situations.
*
* @param string $VN The shorthand virus name.
* @return string The full-length "translated" virus name.
*/
$phpMussel['vn_shorthand'] = function ($VN) use (&$phpMussel) {
$phpMussel['memCache']['weighted'] = false;
$phpMussel['memCache']['ignoreme'] = false;
if ($VN[0] !== "\x1a") {
return $VN;
}
$n = $phpMussel['split_nibble']($VN[1]);
$out = '';
if ($n[0] === 2) {
$out .= 'ClamAV-';
} elseif ($n[0] === 3) {
$out .= 'phpMussel-';
} elseif ($n[0] === 4) {
$out .= 'SecuriteInfo-';
} elseif ($n[0] === 5) {
$out .= 'ZBB-';
} elseif ($n[0] === 6) {
$out .= 'NLNetLabs-';
} elseif ($n[0] === 7) {
$out .= 'FoxIT-';
} elseif ($n[0] === 8) {
if ($n[1] === 0) {
$out .= 'PhishTank-';
} elseif ($n[1] === 1) {
$out .= 'Malc0de-';
} elseif ($n[1] === 2) {
$out .= 'hpHosts-';
} elseif ($n[1] === 3) {
$out .= 'Spam404-';
} elseif ($n[1] === 4) {
$out .= 'Cybercrime.Tracker-';
}
} elseif ($n[0] === 9) {
$phpMussel['memCache']['weighted'] = true;
$out .= 'phpMussel-';
} elseif ($n[0] === 15) {
$phpMussel['memCache']['weighted'] = true;
}
if ($n[0] !== 8) {
if ($n[1] === 1) {
$out .= 'Testfile.';
} elseif ($n[1] === 2) {
$out .= 'FN.';
} elseif ($n[1] === 3) {
$out .= 'VT.';
} elseif ($n[1] === 4) {
$out .= 'META.';
} elseif ($n[1] === 5) {
$out .= 'Chameleon.';
} elseif ($n[1] === 6) {
$out .= 'Werewolf.';
} elseif ($n[1] === 7) {
$out .= 'Suspect.';
} elseif ($n[1] === 8) {
$out .= 'Fake.';
} elseif ($n[1] === 9) {
$out .= 'CVE.';
} elseif ($n[1] === 15) {
$out .= 'HEUR.';
}
}
$n = $phpMussel['split_nibble']($VN[2]);
if ($n[0] === 1) {
if ($n[1] === 1) {
$out .= 'Win.';
} elseif ($n[1] === 2) {
$out .= 'W32.';
} elseif ($n[1] === 3) {
$out .= 'W64.';
} elseif ($n[1] === 4) {
$out .= 'ELF.';
} elseif ($n[1] === 5) {
$out .= 'OSX.';
} elseif ($n[1] === 6) {
$out .= 'Android.';
} elseif ($n[1] === 7) {
$out .= 'Email.';
} elseif ($n[1] === 8) {
$out .= 'JS.';
} elseif ($n[1] === 9) {
$out .= 'Java.';
} elseif ($n[1] === 10) {
$out .= 'XXE.';
} elseif ($n[1] === 11) {
$out .= 'Graphics.';
} elseif ($n[1] === 12) {
$out .= 'OLE.';
} elseif ($n[1] === 13) {
$out .= 'HTML.';
} elseif ($n[1] === 14) {
$out .= 'RTF.';
} elseif ($n[1] === 15) {
$out .= 'Archive.';
}
} elseif ($n[0] === 2) {
if ($n[1] === 0) {
$out .= 'PHP.';
} elseif ($n[1] === 1) {
$out .= 'XML.';
} elseif ($n[1] === 2) {
$out .= 'ASP.';
} elseif ($n[1] === 3) {
$out .= 'VBS.';
} elseif ($n[1] === 4) {
$out .= 'BAT.';
} elseif ($n[1] === 5) {
$out .= 'PDF.';
} elseif ($n[1] === 6) {
$out .= 'SWF.';
} elseif ($n[1] === 7) {
$out .= 'W97M.';
} elseif ($n[1] === 8) {
$out .= 'X97M.';
} elseif ($n[1] === 9) {
$out .= 'O97M.';
} elseif ($n[1] === 10) {
$out .= 'ASCII.';
} elseif ($n[1] === 11) {
$out .= 'Unix.';
} elseif ($n[1] === 12) {
$out .= 'Python.';
} elseif ($n[1] === 13) {
$out .= 'Perl.';
} elseif ($n[1] === 14) {
$out .= 'Ruby.';
} elseif ($n[1] === 15) {
$out .= 'INF/INI.';
}
} elseif ($n[0] === 3) {
if ($n[1] === 0) {
$out .= 'CGI.';
}
}
$n = $phpMussel['split_nibble']($VN[3]);
if ($n[0] === 1) {
if ($n[1] === 1) {
$out .= 'Worm.';
} elseif ($n[1] === 2) {
$out .= 'Trojan.';
} elseif ($n[1] === 3) {
$out .= 'Adware.';
if (!$phpMussel['Config']['signatures']['detect_adware']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 4) {
$out .= 'Flooder.';
} elseif ($n[1] === 5) {
$out .= 'IRCBot.';
} elseif ($n[1] === 6) {
$out .= 'Exploit.';
} elseif ($n[1] === 7) {
$out .= 'VirTool.';
} elseif ($n[1] === 8) {
$out .= 'Dialer.';
} elseif ($n[1] === 9) {
$out .= 'Joke/Hoax.';
if (!$phpMussel['Config']['signatures']['detect_joke_hoax']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 11) {
$out .= 'Malware.';
} elseif ($n[1] === 12) {
$out .= 'Riskware.';
} elseif ($n[1] === 13) {
$out .= 'Rootkit.';
} elseif ($n[1] === 14) {
$out .= 'Backdoor.';
} elseif ($n[1] === 15) {
$out .= 'Hacktool.';
}
} elseif ($n[0] === 2) {
if ($n[1] === 0) {
$out .= 'Keylogger.';
} elseif ($n[1] === 1) {
$out .= 'Ransomware.';
} elseif ($n[1] === 2) {
$out .= 'Spyware.';
} elseif ($n[1] === 3) {
$out .= 'Virus.';
} elseif ($n[1] === 4) {
$out .= 'Dropper.';
} elseif ($n[1] === 5) {
$out .= 'Dropped.';
} elseif ($n[1] === 6) {
$out .= 'Downloader.';
} elseif ($n[1] === 7) {
$out .= 'Obfuscation.';
} elseif ($n[1] === 8) {
$out .= 'Obfuscator.';
} elseif ($n[1] === 9) {
$out .= 'Obfuscated.';
} elseif ($n[1] === 10) {
$out .= 'Packer.';
if (!$phpMussel['Config']['signatures']['detect_packer_packed']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 11) {
$out .= 'Packed.';
if (!$phpMussel['Config']['signatures']['detect_packer_packed']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 12) {
$out .= 'PUA/PUP.';
if (!$phpMussel['Config']['signatures']['detect_pua_pup']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 13) {
$out .= 'Shell.';
if (!$phpMussel['Config']['signatures']['detect_shell']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 14) {
$out .= 'Defacer.';
if (!$phpMussel['Config']['signatures']['detect_deface']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 15) {
$out .= 'Defacement.';
if (!$phpMussel['Config']['signatures']['detect_deface']) {
$phpMussel['memCache']['ignoreme'] = true;
}
}
} elseif ($n[0] === 3) {
if ($n[1] === 0) {
$out .= 'Cryptor.';
} elseif ($n[1] === 1) {
$out .= 'Phish.';
} elseif ($n[1] === 2) {
$out .= 'Spam.';
} elseif ($n[1] === 3) {
$out .= 'Spammer.';
} elseif ($n[1] === 4) {
$out .= 'Scam.';
} elseif ($n[1] === 5) {
$out .= 'ZipBomb.';
} elseif ($n[1] === 6) {
$out .= 'ForkBomb.';
} elseif ($n[1] === 7) {
$out .= 'LogicBomb.';
} elseif ($n[1] === 8) {
$out .= 'CyberBomb.';
} elseif ($n[1] === 9) {
$out .= 'Malvertisement.';
} elseif ($n[1] === 13) {
$out .= 'Encrypted.';
if (!$phpMussel['Config']['signatures']['detect_encryption']) {
$phpMussel['memCache']['ignoreme'] = true;
}
} elseif ($n[1] === 15) {
$out .= 'BadURL.';
}
}
return $out . substr($VN, 4);
};
/**
* Used for performing lookups to the Google Safe Browsing API (v4).
* @link https://developers.google.com/safe-browsing/v4/lookup-api
*
* @param array $urls An array of the URLs to lookup.
* @param array $URLsNoLookup An optional array of URLs to NOT lookup.
* @param array $DomainsNoLookup An optional array of domains to NOT lookup.
* @return int The results of the lookup. 200 if AT LEAST ONE of the queried
* URLs are listed on any of Google Safe Browsing lists; 204 if NONE of
* the queried URLs are listed on any of Google Safe Browsing lists; 400
* if the request is malformed; 401 if the API key is missing or isn't
* authorised; 503 if the service is unavailable (e.g., if it's been
* throttled); 999 if something unexpected occurs (such as, for example,
* if a programmatic error is encountered).
*/
$phpMussel['SafeBrowseLookup'] = function ($urls, $URLsNoLookup = [], $DomainsNoLookup = []) use (&$phpMussel) {
if (empty($phpMussel['Config']['urlscanner']['google_api_key'])) {
return 401;
}
/** Count and prepare the URLs. */
if (!$c = count($urls)) {
return 400;
}
for ($i = 0; $i < $c; $i++) {
$Domain = (strpos($urls[$i], '/') !== false) ? $phpMussel['substrbf']($urls[$i], '/') : $urls[$i];
if (!empty($URLsNoLookup[$urls[$i]]) || !empty($DomainsNoLookup[$Domain])) {
unset($urls[$i]);
continue;
}
$urls[$i] = ['url' => $urls[$i]];
}
sort($urls);
/** After we've prepared the URLs, we prepare our JSON array. */
$arr = json_encode([
'client' => [
'clientId' => 'phpMussel',
'clientVersion' => $phpMussel['ScriptVersion']
],
'threatInfo' => [
'threatTypes' => [
'THREAT_TYPE_UNSPECIFIED',
'MALWARE',
'SOCIAL_ENGINEERING',
'UNWANTED_SOFTWARE',
'POTENTIALLY_HARMFUL_APPLICATION'
],
'platformTypes' => ['ANY_PLATFORM'],
'threatEntryTypes' => ['URL'],
'threatEntries' => $urls
]
], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
/** Fetch the cache entry for Google Safe Browsing, if it doesn't already exist. */
if (!isset($phpMussel['memCache']['urlscanner_google'])) {
$phpMussel['memCache']['urlscanner_google'] = $phpMussel['FetchCache']('urlscanner_google');
}
/** Generate new cache expiry time. */
$newExpiry = $phpMussel['Time'] + $phpMussel['Config']['urlscanner']['cache_time'];
/** Generate a reference for the cache entry for this lookup. */
$cacheRef = md5($arr) . ':' . $c . ':' . strlen($arr) . ':';
/** This will contain the lookup response. */
$response = '';
/** Check if this lookup has already been performed. */
while (substr_count($phpMussel['memCache']['urlscanner_google'], $cacheRef)) {
$response = $phpMussel['substrbf']($phpMussel['substral']($phpMussel['memCache']['urlscanner_google'], $cacheRef), ';');
/** Safety mechanism. */
if (!$response || !substr_count($phpMussel['memCache']['urlscanner_google'], $cacheRef . $response . ';')) {
$response = '';
break;
}
$expiry = $phpMussel['substrbf']($response, ':');
if ($expiry > $phpMussel['Time']) {
$response = $phpMussel['substraf']($response, ':');
break;
}
$phpMussel['memCache']['urlscanner_google'] =
str_ireplace($cacheRef . $response . ';', '', $phpMussel['memCache']['urlscanner_google']);
$response = '';
}
/** If this lookup has already been performed, return the results without repeating it. */
if ($response) {
/** Update the cache entry for Google Safe Browsing. */
$newExpiry = $phpMussel['SaveCache']('urlscanner_google', $newExpiry, $phpMussel['memCache']['urlscanner_google']);
if ($response === '200') {
/** Potentially harmful URL detected. */
return 200;
} elseif ($response === '204') {
/** Potentially harmful URL *NOT* detected. */
return 204;
} elseif ($response === '400') {
/** Bad/malformed request. */
return 400;
} elseif ($response === '401') {
/** Unauthorised (possibly a bad API key). */
return 401;
} elseif ($response === '503') {
/** Service unavailable. */
return 503;
}
/** Something bad/unexpected happened. */
return 999;
}
/** Prepare the URL to use with cURL. */
$uri =
'https://safebrowsing.googleapis.com/v4/threatMatches:find?key=' .
$phpMussel['Config']['urlscanner']['google_api_key'];
/** cURL stuff here. */
$request = curl_init($uri);
curl_setopt($request, CURLOPT_FRESH_CONNECT, true);
curl_setopt($request, CURLOPT_HEADER, false);
curl_setopt($request, CURLOPT_POST, true);
/** Ensure it knows we're sending JSON data. */
curl_setopt($request, CURLOPT_HTTPHEADER, ['Content-type: application/json']);
/** The Google Safe Browsing API requires HTTPS+SSL (there's no way around this). */
curl_setopt($request, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
/*
* Setting "CURLOPT_SSL_VERIFYPEER" to false can be somewhat risky due to man-in-the-middle attacks, but lookups
* seemed to always fail when it was set to true during testing, so, for the sake of this actually working at all,
* I'm setting it as false, but we should try to fix this in the future at some point.
*/
curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false);
/* We don't want to leave the client waiting for *too* long. */
curl_setopt($request, CURLOPT_TIMEOUT, $phpMussel['Timeout']);
curl_setopt($request, CURLOPT_USERAGENT, $phpMussel['ScriptUA']);
curl_setopt($request, CURLOPT_POSTFIELDS, $arr);
/** Execute and get the response. */
$response = curl_exec($request);
$phpMussel['LookupCount']++;
/** Check for errors and print to the screen if there were any. */
if (!$response) {
throw new \Exception(curl_error($request));
}
/** Close the cURL session. */
curl_close($request);
if (substr_count($response, '"matches":')) {
/** Potentially harmful URL detected. */
$returnVal = 200;
} else {
/** Potentially harmful URL *NOT* detected. */
$returnVal = 204;
}
/** Update the cache entry for Google Safe Browsing. */
$phpMussel['memCache']['urlscanner_google'] .= $cacheRef . ':' . $newExpiry . ':' . $returnVal . ';';
$newExpiry = $phpMussel['SaveCache']('urlscanner_google', $newExpiry, $phpMussel['memCache']['urlscanner_google']);
return $returnVal;
};
/**
* Constructs a list of files contained within a PHARable file (in this
* context, a PHARable file is defined as a file of any the following formats:
* TAR, ZIP, PHAR) and returns that list as a string, entries delimited by a
* linefeed (\x0A) and preceeded by an integer representing the depth of the
* entry (in relation to where it exists within the tree of the PHARable file).
*
* @param string $PharFile The PHARable file to analyse.
* @param int $PharDepth An offset for the depth of entries.
* @return string The constructed list (as per described above).
*/
$phpMussel['BuildPharList'] = function ($PharFile, $PharDepth = 0) use (&$phpMussel) {
$PharDepth++;
$Out = '';
$PharDir = scandir('phar://' . $PharFile);
$PharCount = count($PharDir);
for ($PharIter = 0; $PharIter < $PharCount; $PharIter++) {
if (is_dir('phar://' . $PharFile . '/' . $PharDir[$PharIter])) {
$PharDir[$PharIter] = $phpMussel['BuildPharList']($PharFile . '/' . $PharDir[$PharIter], $PharDepth);
} else {
$PharDir[$PharIter] = $PharDepth . ' ' . $PharFile . '/' . $PharDir[$PharIter];
}
$Out .= $PharDir[$PharIter] . "\n";
}
return $Out;
};
/**
* Checks whether signature length is confined within an acceptable limit.
*
* @param int $Length
* @return bool
*/
$phpMussel['ConfineLength'] = function ($Length) {
return ($Length < 4 || $Length > 1024);
};
/**
* Codeblock for detection triggers (appends detection information). Called
* from within the data handler (treat as private).
*/
$phpMussel['Detected'] = function (&$heur, &$lnap, &$VN, &$ofn, &$ofnSafe, &$out, &$flagged, &$md5, &$str_len) use (&$phpMussel) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
if ($phpMussel['memCache']['weighted']) {
$heur['weight']++;
$heur['cli'] .= $lnap . $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$heur['web'] .= $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
return;
}
$out .= $lnap . $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
};
/**
* Confines a string boundary as per rules specified by parameters.
*
* @param string $Data The string.
* @param string|int $Initial The start of the boundary or string initial offset value.
* @param string|int $Terminal The end of the boundary or string terminal offset value.
* @param array $SectionOffsets Section offset values.
*/
$phpMussel['DataConfineByOffsets'] = function (&$Data, &$Initial, &$Terminal, &$SectionOffsets) {
if ($Initial === '*' && $Terminal === '*') {
return;
}
if (substr($Initial, 0, 2) === 'SE') {
$SectionNum = (int)substr($Initial, 2);
$Initial = '*';
$Terminal = '*';
if (isset($SectionOffsets[$SectionNum][0])) {
$Data = substr($Data, $SectionOffsets[$SectionNum][0] * 2);
}
if (isset($SectionOffsets[$SectionNum][1])) {
$Data = substr($Data, 0, $SectionOffsets[$SectionNum][1] * 2);
}
} elseif (substr($Initial, 0, 2) === 'SL') {
$Remainder = strlen($Initial) > 3 && substr($Initial, 2, 1) === '+' ? (substr($Initial, 3) ?: 0) : 0;
$Initial = '*';
$Final = count($SectionOffsets);
if ($Final > 0 && isset($SectionOffsets[$Final - 1][0])) {
$Data = substr($Data, ($SectionOffsets[$Final - 1][0] + $Remainder) * 2);
}
if ($Terminal !== '*' && $Terminal !== 'Z') {
$Data = substr($Data, 0, $Terminal * 2);
$Terminal = '*';
}
} elseif (substr($Initial, 0, 1) === 'S') {
if (($PlusPos = strpos($Initial, '+')) !== false) {
$SectionNum = substr($Initial, 1, $PlusPos - 1) ?: 0;
$Remainder = substr($Initial, $PlusPos + 1) ?: 0;
} else {
$SectionNum = substr($Initial, 1) ?: 0;
$Remainder = 0;
}
$Initial = '*';
if (isset($SectionOffsets[$SectionNum][0])) {
$Data = substr($Data, ($SectionOffsets[$SectionNum][0] + $Remainder) * 2);
}
if ($Terminal !== '*' && $Terminal !== 'Z') {
$Data = substr($Data, 0, $Terminal * 2);
$Terminal = '*';
}
} else {
if ($Initial !== '*' && $Initial !== 'A') {
$Data = substr($Data, $Initial * 2);
$Initial = '*';
}
if ($Terminal !== '*' && $Terminal !== 'Z') {
$Data = substr($Data, 0, $Terminal * 2);
$Terminal = '*';
}
}
};
/**
* Responsible for handling any data fed to it from the recursor. It shouldn't
* be called manually nor from any other contexts. It takes the data given to
* it from the recursor and checks that data against the various signatures of
* phpMussel, before returning the results of those checks back to the
* recursor.
*
* @param string $str Raw binary data to be checked, supplied by the parent
* closure (generally, the contents of files to being scanned).
* @param int $dpt Represents the current depth of recursion from which the
* closure has been called, used for determining how far to indent any
* entries generated for logging and for the display of scan results in
* CLI.
* @param string $ofn Represents the "original filename" of the file being
* scanned (the original filename, in this context, referring to the name
* of the file being scanned as per supplied by the upload client or CLI
* operator, as opposed to the temporary filename assigned by the server
* or any other filename).
* @return array|bool Returns an array containing the results of the scan as
* both an integer (the first element) and as human-readable text (the
* second element), or returns false if any problems occur preventing the
* data handler from completing its normal process.
*/
$phpMussel['DataHandler'] = function ($str = '', $dpt = 0, $ofn = '') use (&$phpMussel) {
/** If the memory cache isn't set at this point, something has gone very wrong. */
if (!isset($phpMussel['memCache'])) {
throw new \Exception(
(!isset($phpMussel['lang']['required_variables_not_defined'])) ?
'[phpMussel] Required variables aren\'t defined: Can\'t continue.' :
'[phpMussel] ' . $phpMussel['lang']['required_variables_not_defined']
);
}
/** Identifies whether the scan target has been flagged for any reason yet. */
$flagged = false;
/** Increment scan depth. */
$dpt++;
/** Controls indenting relating to scan depth for normal logging and for CLI-mode scanning. */
$lnap = str_pad('> ', ($dpt + 1), '-', STR_PAD_LEFT);
/** Output variable (for when the output is a string). */
$out = '';
/** There's no point bothering to scan zero-byte files. */
if (!$str_len = strlen($str)) {
return [1, ''];
}
$md5 = md5($str);
$sha = sha1($str);
$crc = hash('crc32b', $str);
/** $fourcc: First four bytes of the scan target in hexadecimal notation. */
$fourcc = strtolower(bin2hex(substr($str, 0, 4)));
/** $twocc: First two bytes of the scan target in hexadecimal notation. */
$twocc = substr($fourcc, 0, 4);
/**
* $CoExMeta: Contains metadata pertaining to the scan target, intended to
* be used by the "complex extended" signatures.
*/
$CoExMeta =
'$ofn:' . $ofn . ';md5($ofn):' . md5($ofn) . ';$dpt:' . $dpt .
';$str_len:' . $str_len . ';$md5:' . $md5 . ';$sha:' . $sha .
';$crc:' . $crc . ';$fourcc:' . $fourcc . ';$twocc:' . $twocc . ';';
/** Indicates whether a signature is considered a "weighted" signature. */
$phpMussel['memCache']['weighted'] = false;
/** Variables used for weighted signatures and for heuristic analysis. */
$heur = ['detections' => 0, 'weight' => 0, 'cli' => '', 'web' => ''];
/** Scan target has no name? That's a little suspicious. */
if (!$ofn) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ":\n";
$phpMussel['memCache']['detections_count']++;
$out .=
$lnap . $phpMussel['lang']['scan_missing_filename'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_missing_filename'] .
$phpMussel['lang']['_exclamation'];
return [2, $out];
}
/** URL-encoded version of the scan target name. */
$ofnSafe = urlencode($ofn);
/** Generate cache ID. */
$phpMussel['HashCacheData'] = $md5 . md5($ofn);
/** Register object scanned. */
if (isset($phpMussel['cli_args'][1]) && $phpMussel['cli_args'][1] == 'cli_scan') {
$phpMussel['Stats-Increment']('CLI-Scanned', 1);
} else {
$phpMussel['Stats-Increment']($phpMussel['EOF'] ? 'API-Scanned' : 'Web-Scanned', 1);
}
/**
* Check for the existence of a cache entry corresponding to the file
* being scanned, and if it exists, use it instead of scanning the file.
*/
if (isset($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']])) {
if (!$phpMussel['EOF']) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
}
if (!empty($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']][2])) {
$phpMussel['memCache']['detections_count']++;
$out .= $phpMussel['HexSafe']($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']][2]);
if (!empty($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']][3])) {
$phpMussel['whyflagged'] .= $phpMussel['HexSafe']($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']][3]);
}
}
/** Set debug values, if this has been enabled. */
if (isset($phpMussel['DebugArr'])) {
$phpMussel['DebugArrKey'] = count($phpMussel['DebugArr']);
$phpMussel['DebugArr'][$phpMussel['DebugArrKey']] = [
'Filename' => $ofn,
'FromCache' => true,
'Depth' => $dpt,
'Size' => $str_len,
'MD5' => $md5,
'SHA1' => $sha,
'CRC32B' => $crc,
'2CC' => $twocc,
'4CC' => $fourcc,
'ScanPhase' => $phpMussel['memCache']['phase'],
'Container' => $phpMussel['memCache']['container'],
'Results' => !$out ? 1 : 2,
'Output' => $out
];
}
/** Object not flagged. */
if (!$out) {
return [1, ''];
}
/** Register object flagged. */
if (isset($phpMussel['cli_args'][1]) && $phpMussel['cli_args'][1] == 'cli_scan') {
$phpMussel['Stats-Increment']('CLI-Flagged', 1);
} else {
$phpMussel['Stats-Increment']($phpMussel['EOF'] ? 'API-Flagged' : 'Web-Blocked', 1);
}
/** Object flagged. */
return [2, $out];
}
/** Indicates whether we're in CLI-mode. */
$climode = ($phpMussel['Mussel_sapi'] && $phpMussel['Mussel_PHP']) ? 1 : 0;
if (
$phpMussel['Config']['attack_specific']['scannable_threshold'] > 0 &&
$str_len > $phpMussel['ReadBytes']($phpMussel['Config']['attack_specific']['scannable_threshold'])
) {
$str_len = $phpMussel['ReadBytes']($phpMussel['Config']['attack_specific']['scannable_threshold']);
$str = substr($str, 0, $str_len);
$str_cut = 1;
} else {
$str_cut = 0;
}
/** Indicates whether we need to decode the contents of the scan target. */
$decode_or_not = (
(
$phpMussel['Config']['attack_specific']['decode_threshold'] > 0 &&
$str_len > $phpMussel['ReadBytes']($phpMussel['Config']['attack_specific']['decode_threshold'])
) ||
$str_len < 16
) ? 0 : 1;
/** Indicates whether the scan target is greater than 1KB (can sometimes save time for coex). */
$len_kb = ($str_len > 1024) ? 1 : 0;
/** Indicates whether the scan target is greater than half of 1MB (can sometimes save time for coex). */
$len_hmb = ($str_len > 524288) ? 1 : 0;
/** Indicates whether the scan target is greater than 1MB (can sometimes save time for coex). */
$len_mb = ($str_len > 1048576) ? 1 : 0;
/** Indicates whether the scan target is greater than half of 1GB (can sometimes save time for coex). */
$len_hgb = ($str_len > 536870912) ? 1 : 0;
/** Indicates which phase of the scan process we're currently at. */
$phase = $phpMussel['memCache']['phase'];
/** Indicates whether the scan target is a part of a container (and if so, which type of container). */
$container = $phpMussel['memCache']['container'];
/** Indicates whether the scan target possesses the PDF magic number. */
$pdf_magic = ($fourcc == '25504446');
/** Corresponds to the "detect_adware" configuration directive. */
$detect_adware = $phpMussel['Config']['signatures']['detect_adware'] ? 1 : 0;
/** Corresponds to the "detect_encryption" configuration directive. */
$detect_encryption = $phpMussel['Config']['signatures']['detect_encryption'] ? 1 : 0;
/** Corresponds to the "detect_joke_hoax" configuration directive. */
$detect_joke_hoax = $phpMussel['Config']['signatures']['detect_joke_hoax'] ? 1 : 0;
/** Corresponds to the "detect_pua_pup" configuration directive. */
$detect_pua_pup = $phpMussel['Config']['signatures']['detect_pua_pup'] ? 1 : 0;
/** Corresponds to the "detect_packer_packed" configuration directive. */
$detect_packer_packed = $phpMussel['Config']['signatures']['detect_packer_packed'] ? 1 : 0;
/** Corresponds to the "detect_shell" configuration directive. */
$detect_shell = $phpMussel['Config']['signatures']['detect_shell'] ? 1 : 0;
/** Corresponds to the "detect_deface" configuration directive. */
$detect_deface = $phpMussel['Config']['signatures']['detect_deface'] ? 1 : 0;
list($xt, $xts, $gzxt, $gzxts) = $phpMussel['FetchExt']($ofn);
$CoExMeta .= '$xt:' . $xt . ';$xts:' . $xts . ';';
/** Input ($str) as hexadecimal data. */
$str_hex = bin2hex($str);
$str_hex_len = $str_len * 2;
/** Input ($str) normalised. */
$str_norm = $phpMussel['prescan_normalise']($str, false, $decode_or_not);
$str_norm_len = strlen($str_norm);
/** Normalised input ($str_norm) as hexadecimal data. */
$str_hex_norm = bin2hex($str_norm);
$str_hex_norm_len = $str_norm_len * 2;
/** Input ($str) normalised for HTML. */
$str_html = $phpMussel['prescan_normalise']($str, true, $decode_or_not);
$str_html_len = strlen($str_html);
/** HTML normalised input ($str_html) as hexadecimal data. */
$str_hex_html = bin2hex($str_html);
$str_hex_html_len = $str_html_len * 2;
/** Look for potential Linux/ELF indicators. */
$is_elf = (
$fourcc === '7f454c46' ||
$xt === 'elf'
);
/** Look for potential graphics/image indicators. */
$is_graphics = empty($str) ? false : $phpMussel['Indicator-Image']($xt, substr($str_hex, 0, 32));
/** Look for potential HTML indicators. */
$is_html = (
substr_count(',asp*,dht*,hta,htm*,jsp*,php*,sht*,', ',' . $xts . ',') ||
substr_count(',eml,hta,', ',' . $xt . ',') ||
preg_match(
'/3c(?:21646f6374797065|6120|626f6479|68656164|68746d6c|696672616d65|' .
'696d67|6f626a656374|736372697074|7461626c65|7469746c65)/i',
$str_hex_norm
) ||
preg_match(
'/(?:626f6479|68656164|68746d6c|736372697074|7461626c65|7469746c65)3e/i',
$str_hex_norm
)
);
/** Look for potential email indicators. */
$is_email = (
substr_count(',htm*,ema*,', ',' . $xts . ',') ||
$xt === 'eml' ||
preg_match(
'/0a(?:436f6e74656e742d54797065|44617465|46726f6d|4d6573736167652d4944|4d' .
'494d452d56657273696f6e|5265706c792d546f|52657475726e2d50617468|53656e646' .
'572|5375626a656374|546f|582d4d61696c6572)3a20/i',
$str_hex
) ||
preg_match('/0a2d2d.{32}(?:2d2d)?(?:0d)?0a/i', $str_hex)
);
/** Look for potential Mach-O indicators. */
$is_macho = (
$fourcc === 'cafebabe' ||
$fourcc === 'cafed00d' ||
$fourcc === 'cefaedfe' ||
$fourcc === 'cffaedfe' ||
$fourcc === 'feedface' ||
$fourcc === 'feedfacf'
);
/** Look for potential PDF indicators. */
$is_pdf = ($pdf_magic || $xt === 'pdf');
/** Look for potential Shockwave/SWF indicators. */
$is_swf = (
substr_count(',435753,465753,5a5753,', ',' . substr($str_hex, 0, 6) . ',') ||
substr_count(',swf,swt,', ',' . $xt . ',')
);
/** "Infectable"? Used by ClamAV General and ClamAV ASCII signatures. */
$infectable = true;
/** "Asciiable"? Used by all ASCII signatures. */
$asciiable = (bool)$str_hex_norm_len;
/**
* Attempt to confirm/guess whether the data being handled is from an OLE
* file and whether we'll need to parse it through the OLE signatures.
*/
$is_ole = (
!empty($phpMussel['memCache']['file_is_ole']) && (
$str_hex_len < 49152 ||
substr_count(',ole,xml,rels,', ',' . $xt . ',')
)
);
/** Worked by the switch file. */
$fileswitch = 'unassigned';
if (!isset($phpMussel['memCache']['switch.dat'])) {
$phpMussel['memCache']['switch.dat'] = $phpMussel['ReadFileAsArray']($phpMussel['sigPath'] . 'switch.dat', FILE_IGNORE_NEW_LINES);
}
if (!$phpMussel['memCache']['switch.dat']) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_silently']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ":\n";
}
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_signature_file_missing'] .
' (switch.dat)' . $phpMussel['lang']['_exclamation'];
return [-3,
$lnap . $phpMussel['lang']['scan_signature_file_missing'] .
' (switch.dat)' . $phpMussel['lang']['_exclamation_final'] . "\n"
];
}
}
foreach ($phpMussel['memCache']['switch.dat'] as $ThisRule) {
$Switch = (strpos($ThisRule, ';') === false) ? $ThisRule : $phpMussel['substral']($ThisRule, ';');
if (strpos($Switch, '=') === false) {
continue;
}
$Switch = explode('=', preg_replace('/[^\x20-\xff]/', '', $Switch));
if (empty($Switch[0])) {
continue;
}
if (empty($Switch[1])) {
$Switch[1] = false;
}
$theSwitch = $Switch[0];
$ThisRule = (strpos($ThisRule, ';') === false) ? [$ThisRule] : explode(';', $phpMussel['substrbl']($ThisRule, ';'));
foreach ($ThisRule as $Fragment) {
$Fragment = (strpos($Fragment, ':') === false) ? false : explode(':', $Fragment, 7);
if (empty($Fragment[0])) {
continue 2;
}
if ($Fragment[0] === 'LV') {
if (!isset($Fragment[1]) || substr($Fragment[1], 0, 1) !== '$') {
continue 2;
}
$lv_haystack = substr($Fragment[1],1);
if (!isset($$lv_haystack) || is_array($$lv_haystack)) {
continue 2;
}
$lv_haystack = $$lv_haystack;
if ($climode) {
$lv_haystack = $phpMussel['substral']($phpMussel['substral']($lv_haystack, '/'), "\\");
}
$lv_needle = isset($Fragment[2]) ? $Fragment[2] : '';
$pos_A = isset($Fragment[3]) ? $Fragment[3] : 0;
$pos_Z = isset($Fragment[4]) ? $Fragment[4] : 0;
$lv_min = isset($Fragment[5]) ? $Fragment[5] : 0;
$lv_max = isset($Fragment[6]) ? $Fragment[6] : -1;
if (!$phpMussel['lv_match']($lv_needle, $lv_haystack, $pos_A, $pos_Z, $lv_min, $lv_max)) {
continue 2;
}
} elseif (isset($Fragment[2])) {
if (isset($Fragment[3])) {
if ($Fragment[2] === 'A') {
if (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,', ',' . $Fragment[0] . ',') || (
$Fragment[0] === 'FD' &&
!substr_count("\x01" . substr($str_hex, 0, $Fragment[3] * 2), "\x01" . $Fragment[1])
) || (
$Fragment[0] === 'FD-RX' &&
!preg_match('/\A(?:' . $Fragment[1] . ')/i', substr($str_hex, 0, $Fragment[3] * 2))
) || (
$Fragment[0] === 'FD-NORM' &&
!substr_count("\x01" . substr($str_hex_norm, 0, $Fragment[3] * 2), "\x01" . $Fragment[1])
) || (
$Fragment[0] === 'FD-NORM-RX' &&
!preg_match('/\A(?:' . $Fragment[1] . ')/i', substr($str_hex_norm, 0, $Fragment[3] * 2))
)
) {
continue 2;
}
} elseif (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,', ',' . $Fragment[0] . ',') || (
$Fragment[0] === 'FD' &&
!substr_count(substr($str_hex, $Fragment[2] * 2, $Fragment[3] * 2), $Fragment[1])
) || (
$Fragment[0] === 'FD-RX' &&
!preg_match('/(?:' . $Fragment[1] . ')/i', substr($str_hex, $Fragment[2] * 2, $Fragment[3]*2))
) || (
$Fragment[0] === 'FD-NORM' &&
!substr_count(substr($str_hex_norm, $Fragment[2] * 2, $Fragment[3] * 2), $Fragment[1])
) || (
$Fragment[0] === 'FD-NORM-RX' &&
!preg_match('/(?:' . $Fragment[1] . ')/i', substr($str_hex_norm, $Fragment[2] * 2, $Fragment[3]*2))
)
) {
continue 2;
}
} else {
if ($Fragment[2] === 'A') {
if (
!substr_count(',FN,FD,FD-RX,FD-NORM,FD-NORM-RX,', ',' . $Fragment[0] . ',') || (
$Fragment[0] === 'FN' &&
!preg_match('/\A(?:' . $Fragment[1] . ')/i', $ofn)
) || (
$Fragment[0] === 'FD' &&
!substr_count("\x01" . $str_hex, "\x01" . $Fragment[1])
) || (
$Fragment[0] === 'FD-RX' &&
!preg_match('/\A(?:' . $Fragment[1] . ')/i', $str_hex)
) || (
$Fragment[0] === 'FD-NORM' &&
!substr_count("\x01" . $str_hex_norm, "\x01" . $Fragment[1])
) || (
$Fragment[0] === 'FD-NORM-RX' &&
!preg_match('/\A(?:' . $Fragment[1] . ')/i', $str_hex_norm)
)
) {
continue 2;
}
} elseif (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,', ',' . $Fragment[0] . ',') || (
$Fragment[0] === 'FD' &&
!substr_count(substr($str_hex, $Fragment[2] * 2), $Fragment[1])
) || (
$Fragment[0] === 'FD-RX' &&
!preg_match('/(?:' . $Fragment[1] . ')/i', substr($str_hex, $Fragment[2] * 2))
) || (
$Fragment[0] === 'FD-NORM' &&
!substr_count(substr($str_hex_norm, $Fragment[2] * 2), $Fragment[1])
) || (
$Fragment[0] === 'FD-NORM-RX' &&
!preg_match('/(?:' . $Fragment[1] . ')/i', substr($str_hex_norm, $Fragment[2] * 2))
)
) {
continue 2;
}
}
} elseif (
($Fragment[0] === 'FN' && !preg_match('/(?:' . $Fragment[1] . ')/i', $ofn)) ||
($Fragment[0] === 'FS-MIN' && $str_len < $Fragment[1]) ||
($Fragment[0] === 'FS-MAX' && $str_len > $Fragment[1]) ||
($Fragment[0] === 'FD' && !substr_count($str_hex, $Fragment[1])) ||
($Fragment[0] === 'FD-RX' && !preg_match('/(?:' . $Fragment[1] . ')/i', $str_hex)) ||
($Fragment[0] === 'FD-NORM' && !substr_count($str_hex_norm, $Fragment[1])) ||
($Fragment[0] === 'FD-NORM-RX' && !preg_match('/(?:' . $Fragment[1] . ')/i', $str_hex_norm))
) {
continue 2;
} elseif (substr($Fragment[0], 0, 1) === '$') {
$vf = substr($Fragment[0], 1);
if (!isset($$vf) || is_array($$vf) || $$vf != $Fragment[1]) {
continue 2;
}
} elseif (substr($Fragment[0], 0, 2) === '!$') {
$vf = substr($Fragment[0], 2);
if (!isset($$vf) || is_array($$vf) || $$vf == $Fragment[1]) {
continue 2;
}
} elseif (!substr_count(',FN,FS-MIN,FS-MAX,FD,FD-RX,FD-NORM,FD-NORM-RX,', ',' . $Fragment[0] . ',')) {
continue 2;
}
}
if (count($Switch) > 1) {
if ($Switch[1] === 'true') {
$$theSwitch = true;
continue;
}
if ($Switch[1] === 'false') {
$$theSwitch = false;
continue;
}
$$theSwitch = $Switch[1];
} else {
if (!isset($$theSwitch)) {
$$theSwitch = true;
continue;
}
$$theSwitch = (!$$theSwitch);
}
}
unset($theSwitch, $Switch, $ThisRule);
/** Section offsets. */
$SectionOffsets = [];
/** Confirmation of whether or not the file is a valid PE file. */
$is_pe = false;
/** Number of PE sections in the file. */
$NumOfSections = 0;
$PEFileDescription =
$PEFileVersion =
$PEProductName =
$PEProductVersion =
$PECopyright =
$PEOriginalFilename =
$PECompanyName = '';
if (
!empty($phpMussel['memCache']['PE_Sectional']) ||
!empty($phpMussel['memCache']['PE_Extended']) ||
$phpMussel['Config']['attack_specific']['corrupted_exe']
) {
$PEArr = [];
$PEArr['SectionArr'] = [];
if ($twocc === '4d5a') {
$PEArr['Offset'] = $phpMussel['UnpackSafe']('S', substr($str, 60, 4));
$PEArr['Offset'] = $PEArr['Offset'][1];
while (true) {
$PEArr['DoScan'] = true;
if ($PEArr['Offset'] < 1 || $PEArr['Offset'] > 16384 || $PEArr['Offset'] > $str_len) {
$PEArr['DoScan'] = false;
break;
}
$PEArr['Magic'] = substr($str, $PEArr['Offset'], 2);
if ($PEArr['Magic']!=='PE') {
$PEArr['DoScan'] = false;
break;
}
$PEArr['Proc'] = $phpMussel['UnpackSafe']('S', substr($str, $PEArr['Offset'] + 4, 2));
$PEArr['Proc'] = $PEArr['Proc'][1];
if ($PEArr['Proc'] != 0x14c && $PEArr['Proc'] != 0x8664) {
$PEArr['DoScan'] = false;
break;
}
$PEArr['NumOfSections'] = $phpMussel['UnpackSafe']('S', substr($str, $PEArr['Offset'] + 6, 2));
$NumOfSections = $PEArr['NumOfSections'] = $PEArr['NumOfSections'][1];
$CoExMeta .= 'PE_Offset:' . $PEArr['Offset'] . ';PE_Proc:' . $PEArr['Proc'] . ';NumOfSections:' . $NumOfSections . ';';
if ($NumOfSections < 1 || $NumOfSections > 40) {
$PEArr['DoScan'] = false;
}
break;
}
if (!$PEArr['DoScan']) {
if ($phpMussel['Config']['attack_specific']['corrupted_exe']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .=
$lnap . $phpMussel['lang']['corrupted'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['corrupted'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
} else {
$is_pe = true;
$asciiable = false;
$PEArr['OptHdrSize'] = $phpMussel['UnpackSafe']('S', substr($str, $PEArr['Offset'] + 20, 2));
$PEArr['OptHdrSize'] = $PEArr['OptHdrSize'][1];
for ($PEArr['k'] = 0; $PEArr['k'] < $NumOfSections; $PEArr['k']++) {
$PEArr['SectionArr'][$PEArr['k']] = [];
$PEArr['SectionArr'][$PEArr['k']]['SectionHead'] =
substr($str, $PEArr['Offset'] + 24 + $PEArr['OptHdrSize'] + ($PEArr['k'] * 40), $NumOfSections * 40);
$PEArr['SectionArr'][$PEArr['k']]['SectionName'] =
str_ireplace("\x00", '', substr($PEArr['SectionArr'][$PEArr['k']]['SectionHead'], 0, 8));
$PEArr['SectionArr'][$PEArr['k']]['VirtualSize'] =
$phpMussel['UnpackSafe']('S', substr($PEArr['SectionArr'][$PEArr['k']]['SectionHead'], 8, 4));
$PEArr['SectionArr'][$PEArr['k']]['VirtualSize'] =
$PEArr['SectionArr'][$PEArr['k']]['VirtualSize'][1];
$PEArr['SectionArr'][$PEArr['k']]['VirtualAddress'] =
$phpMussel['UnpackSafe']('S', substr($PEArr['SectionArr'][$PEArr['k']]['SectionHead'], 12, 4));
$PEArr['SectionArr'][$PEArr['k']]['VirtualAddress'] =
$PEArr['SectionArr'][$PEArr['k']]['VirtualAddress'][1];
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'] =
$phpMussel['UnpackSafe']('S', substr($PEArr['SectionArr'][$PEArr['k']]['SectionHead'], 16, 4));
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'] =
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'][1];
$PEArr['SectionArr'][$PEArr['k']]['PointerToRawData'] =
$phpMussel['UnpackSafe']('S', substr($PEArr['SectionArr'][$PEArr['k']]['SectionHead'], 20, 4));
$PEArr['SectionArr'][$PEArr['k']]['PointerToRawData'] =
$PEArr['SectionArr'][$PEArr['k']]['PointerToRawData'][1];
$PEArr['SectionArr'][$PEArr['k']]['SectionData'] = substr(
$str,
$PEArr['SectionArr'][$PEArr['k']]['PointerToRawData'],
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData']
);
$SectionOffsets[$PEArr['k']] = [
$PEArr['SectionArr'][$PEArr['k']]['PointerToRawData'],
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData']
];
$PEArr['SectionArr'][$PEArr['k']]['MD5'] =
md5($PEArr['SectionArr'][$PEArr['k']]['SectionData']);
$phpMussel['PEData'] .=
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'] . ':' .
$PEArr['SectionArr'][$PEArr['k']]['MD5'] . ':' . $ofn . '-' .
$PEArr['SectionArr'][$PEArr['k']]['SectionName'] . "\n";
$CoExMeta .=
'SectionName:' . $PEArr['SectionArr'][$PEArr['k']]['SectionName'] .
';VirtualSize:' . $PEArr['SectionArr'][$PEArr['k']]['VirtualSize'] .
';VirtualAddress:' . $PEArr['SectionArr'][$PEArr['k']]['VirtualAddress'] .
';SizeOfRawData:' . $PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'] .
';MD5:' . $PEArr['SectionArr'][$PEArr['k']]['MD5'] . ';';
$PEArr['SectionArr'][$PEArr['k']] =
$PEArr['SectionArr'][$PEArr['k']]['SizeOfRawData'] . ':' .
$PEArr['SectionArr'][$PEArr['k']]['MD5'] . ':';
}
if (substr_count($str, "V\x00a\x00r\x00F\x00i\x00l\x00e\x00I\x00n\x00f\x00o\x00\x00\x00\x00\x00\x24")) {
$PEArr['FINFO'] = $phpMussel['substral']($str, "V\x00a\x00r\x00F\x00i\x00l\x00e\x00I\x00n\x00f\x00o\x00\x00\x00\x00\x00\x24");
if (substr_count($PEArr['FINFO'], "F\x00i\x00l\x00e\x00D\x00e\x00s\x00c\x00r\x00i\x00p\x00t\x00i\x00o\x00n\x00\x00\x00")) {
$PEFileDescription = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "F\x00i\x00l\x00e\x00D\x00e\x00s\x00c\x00r\x00i\x00p\x00t\x00i\x00o\x00n\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "F\x00i\x00l\x00e\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00")) {
$PEFileVersion = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "F\x00i\x00l\x00e\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "P\x00r\x00o\x00d\x00u\x00c\x00t\x00N\x00a\x00m\x00e\x00\x00\x00")) {
$PEProductName = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "P\x00r\x00o\x00d\x00u\x00c\x00t\x00N\x00a\x00m\x00e\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "P\x00r\x00o\x00d\x00u\x00c\x00t\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00")) {
$PEProductVersion = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "P\x00r\x00o\x00d\x00u\x00c\x00t\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "L\x00e\x00g\x00a\x00l\x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00\x00\x00")) {
$PECopyright = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "L\x00e\x00g\x00a\x00l\x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "O\x00r\x00i\x00g\x00i\x00n\x00a\x00l\x00F\x00i\x00l\x00e\x00n\x00a\x00m\x00e\x00\x00\x00")) {
$PEOriginalFilename = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "O\x00r\x00i\x00g\x00i\x00n\x00a\x00l\x00F\x00i\x00l\x00e\x00n\x00a\x00m\x00e\x00\x00\x00"), "\x00\x00\x00")));
}
if (substr_count($PEArr['FINFO'], "C\x00o\x00m\x00p\x00a\x00n\x00y\x00N\x00a\x00m\x00e\x00\x00\x00")) {
$PECompanyName = trim(str_ireplace("\x00", '', $phpMussel['substrbf']($phpMussel['substral']($PEArr['FINFO'], "C\x00o\x00m\x00p\x00a\x00n\x00y\x00N\x00a\x00m\x00e\x00\x00\x00"), "\x00\x00\x00")));
}
$PEArr['FINFO'] = [];
foreach ([
'PEFileDescription',
'PEFileVersion',
'PEProductName',
'PEProductVersion',
'PECopyright',
'PEOriginalFilename',
'PECompanyName'
] as $PEPart) {
if (!empty($$PEPart)) {
$PEArr['FINFO'][] = '$' . $PEPart . ':' . md5($$PEPart) . ':' . strlen($$PEPart) . ':';
}
}
}
}
}
}
/** Look for potential indicators of not being HTML. */
$is_not_html = (!$is_html && ($is_macho || $is_elf || $is_pe));
/** Look for potential indicators of not being PHP. */
$is_not_php = ((
!substr_count(',phar,', ',' . $xt . ',') &&
!substr_count(',php*,', ',' . $xts . ',') &&
!substr_count(',phar,', ',' . $gzxt . ',') &&
!substr_count(',php*,', ',' . $gzxts . ',') &&
!substr_count($str_hex_norm, '3c3f706870')
) || $is_pe);
/** Set debug values, if this has been enabled. */
if (isset($phpMussel['DebugArr'])) {
$phpMussel['DebugArrKey'] = count($phpMussel['DebugArr']);
$phpMussel['DebugArr'][$phpMussel['DebugArrKey']] = [
'Filename' => $ofn,
'FromCache' => false,
'Depth' => $dpt,
'Size' => $str_len,
'MD5' => $md5,
'SHA1' => $sha,
'CRC32B' => $crc,
'2CC' => $twocc,
'4CC' => $fourcc,
'ScanPhase' => $phase,
'Container' => $container,
'FileSwitch' => $fileswitch,
'Is_ELF' => $is_elf,
'Is_Graphics' => $is_graphics,
'Is_HTML' => $is_html,
'Is_Email' => $is_email,
'Is_MachO' => $is_macho,
'Is_PDF' => $is_pdf,
'Is_SWF' => $is_swf,
'Is_PE' => $is_pe,
'Is_Not_HTML' => $is_not_html,
'Is_Not_PHP' => $is_not_php
];
if ($is_pe) {
$phpMussel['DebugArr'][$phpMussel['DebugArrKey']] += [
'NumOfSections' => $NumOfSections,
'PEFileDescription' => $PEFileDescription,
'PEFileVersion' => $PEFileVersion,
'PEProductName' => $PEProductName,
'PEProductVersion' => $PEProductVersion,
'PECopyright' => $PECopyright,
'PEOriginalFilename' => $PEOriginalFilename,
'PECompanyName' => $PECompanyName
];
}
}
/** Plugin hook: "during_scan". */
$phpMussel['Execute_Hook']('during_scan');
/** Begin URL scanner. */
if (
isset($phpMussel['memCache']['URL_Scanner']) ||
!empty($phpMussel['Config']['urlscanner']['lookup_hphosts']) ||
!empty($phpMussel['Config']['urlscanner']['google_api_key'])
) {
$phpMussel['LookupCount'] = 0;
$URLScanner = [
'FixedSource' => preg_replace('~(data|f(ile|tps?)|https?|sftp):~i', "\x01\\1:", str_replace("\\", '/', $str_norm)) . "\x01",
'DomainsNoLookup' => [],
'DomainsCount' => 0,
'Domains' => [],
'DomainPartsNoLookup' => [],
'DomainParts' => [],
'Queries' => [],
'URLsNoLookup' => [],
'URLsCount' => 0,
'URLs' => [],
'URLPartsNoLookup' => [],
'URLParts' => [],
'TLDs' => [],
'Iterable' => 0,
'Matches' => []
];
$URLScanner['c'] = preg_match_all(
'~(?:data|f(ile|tps?)|https?|sftp)://(?:www[0-9]{0,3}\.)?([0-9a-z.-]{1,512})[^0-9a-z.-]~i',
$URLScanner['FixedSource'],
$URLScanner['Matches']
);
for ($URLScanner['i'] = 0; $URLScanner['c'] > $URLScanner['i']; $URLScanner['i']++) {
$URLScanner['DomainParts'][$URLScanner['Iterable']] = $URLScanner['Matches'][2][$URLScanner['i']];
if (substr_count($URLScanner['DomainParts'][$URLScanner['Iterable']], '.')) {
$URLScanner['TLDs'][$URLScanner['Iterable']] =
'TLD:' . $phpMussel['substral']($URLScanner['DomainParts'][$URLScanner['Iterable']], '.') . ':';
}
$URLScanner['This'] =
md5($URLScanner['Matches'][2][$URLScanner['i']]) . ':' .
strlen($URLScanner['Matches'][2][$URLScanner['i']]) . ':';
$URLScanner['Domains'][$URLScanner['Iterable']] = 'DOMAIN:' . $URLScanner['This'];
$URLScanner['DomainsNoLookup'][$URLScanner['Iterable']] = 'DOMAIN-NOLOOKUP:' . $URLScanner['This'];
$URLScanner['Iterable']++;
}
$URLScanner['DomainsNoLookup'] = array_unique($URLScanner['DomainsNoLookup']);
$URLScanner['Domains'] = array_unique($URLScanner['Domains']);
$URLScanner['DomainParts'] = array_unique($URLScanner['DomainParts']);
$URLScanner['TLDs'] = array_unique($URLScanner['TLDs']);
sort($URLScanner['DomainsNoLookup']);
sort($URLScanner['Domains']);
sort($URLScanner['DomainParts']);
sort($URLScanner['TLDs']);
$URLScanner['Iterable'] = 0;
$URLScanner['Matches'] = '';
$URLScanner['c'] = preg_match_all(
'~(?:data|f(ile|tps?)|https?|sftp)://(?:www[0-9]{0,3}\.)?([!#$&-;=?@-\[\]_a-z\~]{1,4096})[^!#$&-;=?@-\[\]_a-z\~]~i',
$URLScanner['FixedSource'],
$URLScanner['Matches']
);
for ($URLScanner['i'] = 0; $URLScanner['c'] > $URLScanner['i']; $URLScanner['i']++) {
$URLScanner['This'] =
md5($URLScanner['Matches'][2][$URLScanner['i']]) . ':' .
strlen($URLScanner['Matches'][2][$URLScanner['i']]) . ':';
$URLScanner['URLsNoLookup'][$URLScanner['Iterable']] = 'URL-NOLOOKUP:' . $URLScanner['This'];
$URLScanner['URLParts'][$URLScanner['Iterable']] = $URLScanner['Matches'][2][$URLScanner['i']];
$URLScanner['URLs'][$URLScanner['Iterable']] = 'URL:' . $URLScanner['This'];
$URLScanner['Iterable']++;
if (preg_match('/[^0-9a-z.-]$/i', $URLScanner['Matches'][2][$URLScanner['i']])) {
$URLScanner['x'] =
preg_replace('/[^0-9a-z.-]+$/i', '', $URLScanner['Matches'][2][$URLScanner['i']]);
$URLScanner['This'] = md5($URLScanner['x']) . ':' . strlen($URLScanner['x']) . ':';
$URLScanner['URLsNoLookup'][$URLScanner['Iterable']] = 'URL-NOLOOKUP:' . $URLScanner['This'];
$URLScanner['URLParts'][$URLScanner['Iterable']] = $URLScanner['x'];
$URLScanner['URLs'][$URLScanner['Iterable']] = 'URL:' . $URLScanner['This'];
$URLScanner['Iterable']++;
}
if (substr_count($URLScanner['Matches'][2][$URLScanner['i']], '?')) {
$URLScanner['x'] = $phpMussel['substrbf']($URLScanner['Matches'][2][$URLScanner['i']], '?');
$URLScanner['This'] = md5($URLScanner['x']) . ':' . strlen($URLScanner['x']) . ':';
$URLScanner['URLsNoLookup'][$URLScanner['Iterable']] = 'URL-NOLOOKUP:' . $URLScanner['This'];
$URLScanner['URLParts'][$URLScanner['Iterable']] = $URLScanner['x'];
$URLScanner['URLs'][$URLScanner['Iterable']] = 'URL:' . $URLScanner['This'];
$URLScanner['x'] = $phpMussel['substraf']($URLScanner['Matches'][2][$URLScanner['i']], '?');
$URLScanner['Queries'][$URLScanner['Iterable']] =
'QUERY:' . md5($URLScanner['x']) . ':' .
strlen($URLScanner['x']) . ':';
$URLScanner['Iterable']++;
}
}
$URLScanner['Matches'] = '';
$URLScanner['URLsNoLookup'] = array_unique($URLScanner['URLsNoLookup']);
$URLScanner['URLs'] = array_unique($URLScanner['URLs']);
$URLScanner['URLParts'] = array_unique($URLScanner['URLParts']);
$URLScanner['Queries'] = array_unique($URLScanner['Queries']);
sort($URLScanner['URLsNoLookup']);
sort($URLScanner['URLs']);
sort($URLScanner['URLParts']);
sort($URLScanner['Queries']);
}
/** Process non-mappable signatures. */
foreach ([
['General_Command_Detections', 0],
['Hash', 1],
['PE_Sectional', 2],
['PE_Extended', 3],
['URL_Scanner', 4],
['Complex_Extended', 5]
] as $ThisConf) {
$SigFiles = isset($phpMussel['memCache'][$ThisConf[0]]) ? explode(',', $phpMussel['memCache'][$ThisConf[0]]) : [];
foreach ($SigFiles as $SigFile) {
if (!$SigFile) {
continue;
}
if (!isset($phpMussel['memCache'][$SigFile])) {
$phpMussel['memCache'][$SigFile] = $phpMussel['ReadFile']($phpMussel['sigPath'] . $SigFile);
}
if (!$phpMussel['memCache'][$SigFile]) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_silently']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ":\n";
}
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_signature_file_missing'] .
' (' . $SigFile . ')' .
$phpMussel['lang']['_exclamation'];
return [-3,
$lnap . $phpMussel['lang']['scan_signature_file_missing'] .
' (' . $SigFile . ')' .
$phpMussel['lang']['_exclamation_final'] . "\n"
];
}
} elseif ($ThisConf[1] === 0) {
if (substr($phpMussel['memCache'][$SigFile], 0, 9) === 'phpMussel') {
$phpMussel['memCache'][$SigFile] = substr($phpMussel['memCache'][$SigFile], 11, -1);
}
$ArrayCSV = explode(',', $phpMussel['memCache'][$SigFile]);
foreach ($ArrayCSV as $ItemCSV) {
if (substr_count($str_hex_norm, $ItemCSV)) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .=
$lnap . $phpMussel['lang']['scan_command_injection'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_command_injection'] . ', \'' .
$phpMussel['HexSafe']($ItemCSV) .
'\' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
unset($ItemCSV, $ArrayCSV);
} elseif ($ThisConf[1] === 1) {
if (substr_count($phpMussel['memCache'][$SigFile], $md5 . ':' . $str_len . ':')) {
$xsig = $phpMussel['substraf']($phpMussel['memCache'][$SigFile], $md5 . ':' . $str_len . ':');
if (substr_count($xsig, "\n")) {
$xsig = $phpMussel['substrbf']($xsig, "\n");
}
$xsig = $phpMussel['vn_shorthand']($xsig);
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $xsig . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$phpMussel['Detected']($heur, $lnap, $xsig, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
}
} elseif ($ThisConf[1] === 2) {
for ($PEArr['k'] = 0; $PEArr['k'] < $NumOfSections; $PEArr['k']++) {
if (substr_count($phpMussel['memCache'][$SigFile], $PEArr['SectionArr'][$PEArr['k']])) {
$xsig = $phpMussel['substraf']($phpMussel['memCache'][$SigFile], $PEArr['SectionArr'][$PEArr['k']]);
if (substr_count($xsig, "\n")) {
$xsig = $phpMussel['substrbf']($xsig, "\n");
}
$xsig = $phpMussel['vn_shorthand']($xsig);
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $xsig . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$phpMussel['Detected']($heur, $lnap, $xsig, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
}
}
} elseif ($ThisConf[1] === 3) {
if (!empty($PEArr['FINFO'])) {
foreach ($PEArr['FINFO'] as $PEArr['ThisPart']) {
if (substr_count($phpMussel['memCache'][$SigFile], $PEArr['ThisPart'])) {
$xsig = $phpMussel['substraf']($phpMussel['memCache'][$SigFile], $PEArr['ThisPart']);
if (substr_count($xsig, "\n")) {
$xsig = $phpMussel['substrbf']($xsig, "\n");
}
$xsig = $phpMussel['vn_shorthand']($xsig);
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $xsig . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$phpMussel['Detected']($heur, $lnap, $xsig, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
}
}
}
} elseif ($ThisConf[1] === 4) {
foreach ([$URLScanner['DomainsNoLookup'], $URLScanner['URLsNoLookup']] as $URLScanner['ThisArr']) {
foreach ($URLScanner['ThisArr'] as $URLScanner['This']) {
if (substr_count($phpMussel['memCache'][$SigFile], $URLScanner['This'])) {
$xsig = $phpMussel['substraf']($phpMussel['memCache'][$SigFile], $URLScanner['This']);
if (substr_count($xsig, "\n")) {
$xsig = $phpMussel['substrbf']($xsig, "\n");
}
if (substr($URLScanner['This'], 0, 15) === 'DOMAIN-NOLOOKUP') {
$URLScanner['DomainPartsNoLookup'][$xsig] = true;
} else {
$URLScanner['URLPartsNoLookup'][$xsig] = true;
}
}
}
}
foreach ([
$URLScanner['TLDs'],
$URLScanner['Domains'],
$URLScanner['URLs'],
$URLScanner['Queries']
] as $URLScanner['ThisArr']) {
foreach ($URLScanner['ThisArr'] as $URLScanner['This']) {
if (substr_count($phpMussel['memCache'][$SigFile], $URLScanner['This'])) {
$xsig = $phpMussel['substraf']($phpMussel['memCache'][$SigFile], $URLScanner['This']);
if (substr_count($xsig, "\n")) {
$xsig = $phpMussel['substrbf']($xsig, "\n");
}
if (
($xsig = $phpMussel['vn_shorthand']($xsig)) &&
!substr_count($phpMussel['memCache']['greylist'], ',' . $xsig . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$phpMussel['Detected']($heur, $lnap, $xsig, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
}
}
}
} elseif ($ThisConf[1] === 5) {
$coexi = 0;
$SigName = '';
while (true) {
$coexi++;
if (
$coexi === 1 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$md5:' . $md5 . ';')
) {
$xsig = explode("\n" . '$md5:' . $md5 . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 2 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$str_len:' . $str_len . ';')
) {
$xsig = explode("\n" . '$str_len:' . $str_len . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 3 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$fourcc:' . $fourcc . ';')
) {
$xsig = explode("\n" . '$fourcc:' . $fourcc . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 4 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$twocc:' . $twocc . ';')
) {
$xsig = explode("\n" . '$twocc:' . $twocc . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 5 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$phase:' . $phase . ';')
) {
$xsig = explode("\n" . '$phase:' . $phase . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 6 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$xt:' . $xt . ';')
) {
$xsig = explode("\n" . '$xt:' . $xt . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 7 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$sha:' . $sha . ';')
) {
$xsig = explode("\n" . '$sha:' . $sha . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 8 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$crc:' . $crc . ';')
) {
$xsig = explode("\n" . '$crc:' . $crc . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 9 &&
$is_html &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_html:1;')
) {
$xsig = explode("\n" . '$is_html:1;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 9 &&
!$is_html &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_html:0;')
) {
$xsig = explode("\n" . '$is_html:0;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 10 &&
$is_graphics &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_graphics:1;')
) {
$xsig = explode("\n" . '$is_graphics:1;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 10 &&
!$is_graphics &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_graphics:0;')
) {
$xsig = explode("\n" . '$is_graphics:0;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 11 &&
$is_pe &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_pe:1;')
) {
$xsig = explode("\n" . '$is_pe:1;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 11 &&
!$is_pe &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_pe:0;')
) {
$xsig = explode("\n" . '$is_pe:0;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 12 &&
$is_macho &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_macho:1;')
) {
$xsig = explode("\n" . '$is_macho:1;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 12 &&
!$is_macho &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$is_macho:0;')
) {
$xsig = explode("\n" . '$is_macho:0;', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 13 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$xts:' . $xts . ';')
) {
$xsig = explode("\n" . '$xts:' . $xts . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 14 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$NumOfSections:' . $NumOfSections . ';')
) {
$xsig = explode("\n" . '$NumOfSections:' . $NumOfSections . ';', $phpMussel['memCache'][$SigFile]);
} elseif (
$coexi === 15 &&
substr_count($phpMussel['memCache'][$SigFile], "\n" . '$container:' . $container . ';')
) {
$xsig = explode("\n" . '$container:' . $container . ';', $phpMussel['memCache'][$SigFile]);
} elseif ($coexi > 15) {
break;
} else {
$xsig = [];
}
$xc = count($xsig);
if (isset($xsig[0])) {
$xsig[0] = '';
}
if ($xc > 0) {
for ($xi = 1; $xi < $xc; $xi++) {
if (substr_count($xsig[$xi], "\n")) {
$xsig[$xi] = $phpMussel['substrbf']($xsig[$xi], "\n");
}
if (substr_count($xsig[$xi], ';')) {
if (!substr_count($xsig[$xi], ':')) {
continue;
}
$SigName = $phpMussel['vn_shorthand']($phpMussel['substral']($xsig[$xi], ';'));
$xsig[$xi] = explode(';', $phpMussel['substrbl']($xsig[$xi], ';'));
$sxc = count($xsig[$xi]);
} else {
$SigName = $phpMussel['vn_shorthand']($xsig[$xi]);
$xsig[$xi] = '';
$sxc = 0;
}
if ($sxc > 0) {
for ($sxi = 0; $sxi < $sxc; $sxi++) {
$xsig[$xi][$sxi] = explode(':', $xsig[$xi][$sxi], 7);
if ($xsig[$xi][$sxi][0] === 'LV') {
if (!isset($xsig[$xi][$sxi][1]) || substr($xsig[$xi][$sxi][1], 0, 1) !== '$') {
continue 2;
}
$lv_haystack = substr($xsig[$xi][$sxi][1], 1);
if (!isset($$lv_haystack) || is_array($$lv_haystack)) {
continue 2;
}
$lv_haystack = $$lv_haystack;
if ($climode) {
$lv_haystack = $phpMussel['substral']($phpMussel['substral']($lv_haystack, '/'), "\\");
}
$lv_needle = (isset($xsig[$xi][$sxi][2])) ? $xsig[$xi][$sxi][2] : '';
$pos_A = (isset($xsig[$xi][$sxi][3])) ? $xsig[$xi][$sxi][3] : 0;
$pos_Z = (isset($xsig[$xi][$sxi][4])) ? $xsig[$xi][$sxi][4] : 0;
$lv_min = (isset($xsig[$xi][$sxi][5])) ? $xsig[$xi][$sxi][5] : 0;
$lv_max = (isset($xsig[$xi][$sxi][6])) ? $xsig[$xi][$sxi][6] : -1;
if (!$phpMussel['lv_match']($lv_needle, $lv_haystack, $pos_A, $pos_Z, $lv_min, $lv_max)) {
continue 2;
}
} elseif (isset($xsig[$xi][$sxi][2])) {
if (isset($xsig[$xi][$sxi][3])) {
if ($xsig[$xi][$sxi][2] == 'A') {
if (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,META,', ',' . $xsig[$xi][$sxi][0] . ',') || (
$xsig[$xi][$sxi][0] == 'FD' &&
!substr_count("\x01" . substr($str_hex, 0, $xsig[$xi][$sxi][3] * 2), "\x01" . $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-RX' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex, 0, $xsig[$xi][$sxi][3] * 2))
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM' &&
!substr_count("\x01" . substr($str_hex_norm, 0, $xsig[$xi][$sxi][3] * 2), "\x01" . $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM-RX' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex_norm, 0, $xsig[$xi][$sxi][3] * 2))
) || (
$xsig[$xi][$sxi][0] == 'META' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($CoExMeta, 0, $xsig[$xi][$sxi][3] * 2))
)
) {
continue 2;
}
} else {
if (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,META,', ',' . $xsig[$xi][$sxi][0] . ',') || (
$xsig[$xi][$sxi][0] == 'FD' &&
!substr_count(substr($str_hex, $xsig[$xi][$sxi][2] * 2, $xsig[$xi][$sxi][3] * 2), $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex, $xsig[$xi][$sxi][2] * 2, $xsig[$xi][$sxi][3] * 2))
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM' &&
!substr_count(substr($str_hex_norm, $xsig[$xi][$sxi][2] * 2, $xsig[$xi][$sxi][3] * 2), $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex_norm, $xsig[$xi][$sxi][2] * 2, $xsig[$xi][$sxi][3] * 2))
) || (
$xsig[$xi][$sxi][0] == 'META' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($CoExMeta, $xsig[$xi][$sxi][2] * 2, $xsig[$xi][$sxi][3] * 2))
)
) {
continue 2;
}
}
} else {
if ($xsig[$xi][$sxi][2] == 'A') {
if (
!substr_count(',FN,FD,FD-RX,FD-NORM,FD-NORM-RX,META,', ',' . $xsig[$xi][$sxi][0] . ',') || (
$xsig[$xi][$sxi][0] == 'FN' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', $ofn)
) || (
$xsig[$xi][$sxi][0] == 'FD' &&
!substr_count("\x01" . $str_hex, "\x01" . $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-RX' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', $str_hex)
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM' &&
!substr_count("\x01" . $str_hex_norm, "\x01" . $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM-RX' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', $str_hex_norm)
) || (
$xsig[$xi][$sxi][0] == 'META' &&
!preg_match('/\A(?:' . $xsig[$xi][$sxi][1] . ')/i', $CoExMeta)
)
) {
continue 2;
}
} else {
if (
!substr_count(',FD,FD-RX,FD-NORM,FD-NORM-RX,META,', ',' . $xsig[$xi][$sxi][0] . ',') || (
$xsig[$xi][$sxi][0] == 'FD' &&
!substr_count(substr($str_hex, $xsig[$xi][$sxi][2] * 2), $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex, $xsig[$xi][$sxi][2] * 2))
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM' &&
!substr_count(substr($str_hex_norm, $xsig[$xi][$sxi][2] * 2), $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($str_hex_norm, $xsig[$xi][$sxi][2] * 2))
) || (
$xsig[$xi][$sxi][0] == 'META' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', substr($CoExMeta, $xsig[$xi][$sxi][2] * 2))
)
) {
continue 2;
}
}
}
} else {
if (
(
$xsig[$xi][$sxi][0] == 'FN' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', $ofn)
) || (
$xsig[$xi][$sxi][0] == 'FS-MIN' &&
$str_len < $xsig[$xi][$sxi][1]
) || (
$xsig[$xi][$sxi][0] == 'FS-MAX' &&
$str_len > $xsig[$xi][$sxi][1]
) || (
$xsig[$xi][$sxi][0] == 'FD' &&
!substr_count($str_hex, $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', $str_hex)
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM' &&
!substr_count($str_hex_norm, $xsig[$xi][$sxi][1])
) || (
$xsig[$xi][$sxi][0] == 'FD-NORM-RX' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', $str_hex_norm)
) || (
$xsig[$xi][$sxi][0] == 'META' &&
!preg_match('/(?:' . $xsig[$xi][$sxi][1] . ')/i', $CoExMeta)
)
) {
continue 2;
}
if (substr($xsig[$xi][$sxi][0], 0, 1) == '$') {
$vf = substr($xsig[$xi][$sxi][0], 1);
if (!isset($$vf) || is_array($$vf) || $$vf != $xsig[$xi][$sxi][1]) {
continue 2;
}
} elseif (substr($xsig[$xi][$sxi][0], 0, 2) == '!$') {
$vf = substr($xsig[$xi][$sxi][0], 2);
if (!isset($$vf) || is_array($$vf) || $$vf == $xsig[$xi][$sxi][1]) {
continue 2;
}
} elseif (!substr_count(',FN,FS-MIN,FS-MAX,FD,FD-RX,FD-NORM,FD-NORM-RX,META,', ',' . $xsig[$xi][$sxi][0] . ',')) {
continue 2;
}
}
}
}
if (
$SigName &&
!substr_count($phpMussel['memCache']['greylist'], ',' . $SigName . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$phpMussel['Detected']($heur, $lnap, $SigName, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
$xsig[$xi] = '';
}
}
}
$sxi = $sxc = $SigName = $xi = $xc = $xsig = $coexi = '';
}
}
}
/** Process mappable signatures. */
foreach ([
['Filename', 'str_hex', 'str_hex_len', 2],
['Standard', 'str_hex', 'str_hex_len', 0],
['Normalised', 'str_hex_norm', 'str_hex_norm_len', 0],
['HTML', 'str_hex_html', 'str_hex_html_len', 0],
['Standard_RegEx', 'str_hex', 'str_hex_len', 1],
['Normalised_RegEx', 'str_hex_norm', 'str_hex_norm_len', 1],
['HTML_RegEx', 'str_hex_html', 'str_hex_html_len', 1]
] as $ThisConf) {
$DataSource = $ThisConf[1];
$DataSourceLen = $ThisConf[2];
$SigFiles = isset($phpMussel['memCache'][$ThisConf[0]]) ? explode(',', $phpMussel['memCache'][$ThisConf[0]]) : [];
foreach ($SigFiles as $SigFile) {
if (!$SigFile) {
continue;
}
if (!isset($phpMussel['memCache'][$SigFile])) {
$phpMussel['memCache'][$SigFile] = $phpMussel['ReadFileAsArray']($phpMussel['sigPath'] . $SigFile, FILE_IGNORE_NEW_LINES);
}
if (!$phpMussel['memCache'][$SigFile]) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_silently']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ":\n";
}
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_signature_file_missing'] .
' (' . $SigFile . ')' . $phpMussel['lang']['_exclamation'];
return [-3,
$lnap . $phpMussel['lang']['scan_signature_file_missing'] .
' (' . $SigFile . ')' . $phpMussel['lang']['_exclamation_final'] . "\n"
];
}
} else {
$NumSigs = count($phpMussel['memCache'][$SigFile]);
for ($SigNum = 0; $SigNum < $NumSigs; $SigNum++) {
if (!$ThisSig = $phpMussel['memCache'][$SigFile][$SigNum]) {
continue;
}
if (substr($ThisSig, 0, 1) == '>') {
$ThisSig = explode('>', $ThisSig, 4);
if (!isset($ThisSig[1]) || !isset($ThisSig[2]) || !isset($ThisSig[3])) {
break;
}
$ThisSig[3] = (int)$ThisSig[3];
if ($ThisSig[1] == 'FN') {
if (!preg_match('/(?:' . $ThisSig[2] . ')/i', $ofn)) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
} elseif ($ThisSig[1] == 'FS-MIN') {
if ($str_len < $ThisSig[2]) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
} elseif ($ThisSig[1] == 'FS-MAX') {
if ($str_len > $ThisSig[2]) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
} elseif ($ThisSig[1] == 'FD') {
if (!substr_count($$DataSource, $ThisSig[2])) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
} elseif ($ThisSig[1] == 'FD-RX') {
if (!preg_match('/(?:' . $ThisSig[2] . ')/i', $$DataSource)) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
} elseif (substr($ThisSig[1], 0, 1) == '$') {
$vf = substr($ThisSig[1], 1);
if (isset($$vf) && !is_array($$vf)) {
if ($$vf != $ThisSig[2]) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
continue;
}
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
} elseif (substr($ThisSig[1], 0, 2) == '!$') {
$vf = substr($ThisSig[1], 2);
if (isset($$vf) && !is_array($$vf)) {
if ($$vf == $ThisSig[2]) {
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
}
continue;
}
if ($ThisSig[3] <= $SigNum) {
break;
}
$SigNum = $ThisSig[3] - 1;
} else {
break;
}
continue;
}
if (strpos($ThisSig, ':') !== false) {
$VN = explode(':', $ThisSig);
if ($ThisConf[3] === 0) {
$ThisSig = preg_split('/[^a-fA-F0-9>]+/i', $VN[1], -1, PREG_SPLIT_NO_EMPTY);
$ThisSig = ($ThisSig === false ? '' : implode('', $ThisSig));
$ThisSigLen = strlen($ThisSig);
if ($phpMussel['ConfineLength']($ThisSigLen)) {
continue;
}
$xstrf = isset($VN[2]) ? $VN[2] : '*';
$xstrt = isset($VN[3]) ? $VN[3] : '*';
$VN = $phpMussel['vn_shorthand']($VN[0]);
$VNLC = strtolower($VN);
if (
($is_not_php && (
substr_count($VNLC, '-php') ||
substr_count($VNLC, '.php')
)) ||
($is_not_html && (
substr_count($VNLC, '-htm') ||
substr_count($VNLC, '.htm')
)) ||
$$DataSourceLen < $ThisSigLen
) {
continue;
}
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $VN . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$ThisSig = strpos($ThisSig, '>') !== false ? explode('>', $ThisSig) : [$ThisSig];
$ThisSigCount = count($ThisSig);
$ThisString = $$DataSource;
$phpMussel['DataConfineByOffsets']($ThisString, $xstrf, $xstrt, $SectionOffsets);
if ($xstrf === 'A') {
$ThisString = "\x01" . $ThisString;
$ThisSig[0] = "\x01" . $ThisSig[0];
}
if ($xstrt === 'Z') {
$ThisString .= "\x01";
$ThisSig[$ThisSigCount - 1] .= "\x01";
}
for ($ThisSigi = 0; $ThisSigi < $ThisSigCount; $ThisSigi++) {
if (strpos($ThisString, $ThisSig[$ThisSigi]) === false) {
continue 2;
}
if ($ThisSigCount > 1 && strpos($ThisString, $ThisSig[$ThisSigi]) !== false) {
$ThisString = $phpMussel['substraf']($ThisString, $ThisSig[$ThisSigi]);
}
}
$phpMussel['Detected']($heur, $lnap, $VN, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
} elseif ($ThisConf[3] === 1) {
$ThisSig = preg_split('/[\x00-\x1f]+/', $VN[1], -1, PREG_SPLIT_NO_EMPTY);
$ThisSig = ($ThisSig === false) ? '' : implode('', $ThisSig);
$ThisSigLen = strlen($ThisSig);
if ($phpMussel['ConfineLength']($ThisSigLen)) {
continue;
}
$xstrf = isset($VN[2]) ? $VN[2] : '*';
$xstrt = isset($VN[3]) ? $VN[3] : '*';
$VN = $phpMussel['vn_shorthand']($VN[0]);
$VNLC = strtolower($VN);
if (
($is_not_php && (
substr_count($VNLC, '-php') ||
substr_count($VNLC, '.php')
)) ||
($is_not_html && (
substr_count($VNLC, '-htm') ||
substr_count($VNLC, '.htm')
))
) {
continue;
}
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $VN . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
$ThisString = $$DataSource;
$phpMussel['DataConfineByOffsets']($ThisString, $xstrf, $xstrt, $SectionOffsets);
if ($xstrf === 'A') {
if ($xstrt === 'Z') {
if (!preg_match('/\A(?:' . $ThisSig . ')$/i', $ThisString)) {
continue;
}
} elseif (!preg_match('/\A(?:' . $ThisSig . ')/i', $ThisString)) {
continue;
}
} else {
if ($xstrt === 'Z') {
if (!preg_match('/(?:' . $ThisSig . ')$/i', $ThisString)) {
continue;
}
} elseif (!preg_match('/(?:' . $ThisSig . ')/i', $ThisString)) {
continue;
}
}
$phpMussel['Detected']($heur, $lnap, $VN, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
} elseif ($ThisConf[3] === 2) {
$ThisSig = preg_split('/[\x00-\x1f]+/', $VN[1], -1, PREG_SPLIT_NO_EMPTY);
$ThisSig = ($ThisSig === false) ? '' : implode('', $ThisSig);
$VN = $phpMussel['vn_shorthand']($VN[0]);
if (
$ThisSig &&
!substr_count($phpMussel['memCache']['greylist'], ',' . $VN . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
if (preg_match('/(?:' . $ThisSig . ')/i', $ofn)) {
$phpMussel['Detected']($heur, $lnap, $VN, $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
}
}
}
}
}
}
}
/** Perform API lookups for domains. */
if (isset($URLScanner) && !$out) {
$URLScanner['DomainsCount'] = count($URLScanner['DomainParts']);
/** Codeblock for performing hpHosts API lookups. */
if ($phpMussel['Config']['urlscanner']['lookup_hphosts'] && $URLScanner['DomainsCount']) {
/** Fetch the cache entry for hpHosts, if it doesn't already exist. */
if (!isset($phpMussel['memCache']['urlscanner_domains'])) {
$phpMussel['memCache']['urlscanner_domains'] = $phpMussel['FetchCache']('urlscanner_domains');
}
$URLScanner['y'] = $phpMussel['Time'] + $phpMussel['Config']['urlscanner']['cache_time'];
$URLScanner['ScriptIdentEncoded'] = urlencode($phpMussel['ScriptIdent']);
$URLScanner['classes'] = [
'EMD' => "\x1a\x82\x10\x1bXXX",
'EXP' => "\x1a\x82\x10\x16XXX",
'GRM' => "\x1a\x82\x10\x32XXX",
'HFS' => "\x1a\x82\x10\x32XXX",
'PHA' => "\x1a\x82\x10\x32XXX",
'PSH' => "\x1a\x82\x10\x31XXX"
];
for ($i = 0; $i < $URLScanner['DomainsCount']; $i++) {
if (!empty($URLScanner['DomainPartsNoLookup'][$URLScanner['DomainParts'][$i]])) {
continue;
}
if (
$phpMussel['Config']['urlscanner']['maximum_api_lookups'] > 0 &&
$phpMussel['LookupCount'] > $phpMussel['Config']['urlscanner']['maximum_api_lookups']
) {
if ($phpMussel['Config']['urlscanner']['maximum_api_lookups_response']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$out .=
$lnap . $phpMussel['lang']['too_many_urls'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['too_many_urls'] .
' (' . $ofnSafe . ')' .
$phpMussel['lang']['_exclamation'];
}
break;
}
$URLScanner['This'] = md5($URLScanner['DomainParts'][$i]) . ':' . strlen($URLScanner['DomainParts'][$i]) . ':';
while (substr_count($phpMussel['memCache']['urlscanner_domains'], $URLScanner['This'])) {
$URLScanner['Class'] =
$phpMussel['substrbf']($phpMussel['substral']($phpMussel['memCache']['urlscanner_domains'], $URLScanner['This']), ';');
if (!substr_count($phpMussel['memCache']['urlscanner_domains'], $URLScanner['This'] . ':' . $URLScanner['Class'] . ';')) {
break;
}
$URLScanner['Expiry'] = (int)$phpMussel['substrbf']($URLScanner['Class'], ':');
if ($URLScanner['Expiry'] > $phpMussel['Time']) {
$URLScanner['Class'] = $phpMussel['substraf']($URLScanner['Class'], ':');
if (!$URLScanner['Class']) {
continue 2;
}
$URLScanner['Class'] = $phpMussel['vn_shorthand']($URLScanner['Class']);
$phpMussel['Detected']($heur, $lnap, $URLScanner['Class'], $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
$phpMussel['memCache']['urlscanner_domains'] =
str_ireplace($URLScanner['This'] . $URLScanner['Class'] . ';', '', $phpMussel['memCache']['urlscanner_domains']);
}
$URLScanner['req'] =
'v=' . $URLScanner['ScriptIdentEncoded'] .
'&s=' . $URLScanner['DomainParts'][$i] .
'&class=true';
$URLScanner['req_result'] = $phpMussel['Request'](
'http://verify.hosts-file.net/?' . $URLScanner['req'],
['v' => $URLScanner['ScriptIdentEncoded'], 's' => $URLScanner['DomainParts'][$i], 'Class' => true],
12
);
$phpMussel['LookupCount']++;
if (substr($URLScanner['req_result'], 0, 6) == "Listed") {
$URLScanner['Class'] = substr($URLScanner['req_result'], 7, 3);
$URLScanner['Class'] = isset($URLScanner['classes'][$URLScanner['Class']]) ?
$URLScanner['classes'][$URLScanner['Class']] : "\x1a\x82\x10\x3fXXX";
$phpMussel['memCache']['urlscanner_domains'] .=
$URLScanner['This'] .
$URLScanner['y'] . ':' .
$URLScanner['Class'] . ';';
$URLScanner['Class'] = $phpMussel['vn_shorthand']($URLScanner['Class']);
$phpMussel['Detected']($heur, $lnap, $URLScanner['Class'], $ofn, $ofnSafe, $out, $flagged, $md5, $str_len);
}
$phpMussel['memCache']['urlscanner_domains'] .= $URLScanner['Domains'][$i] . $URLScanner['y'] . ':;';
}
$phpMussel['SaveCache']('urlscanner_domains', $URLScanner['y'], $phpMussel['memCache']['urlscanner_domains']);
}
$URLScanner['URLsCount'] = count($URLScanner['URLParts']);
/** Codeblock for performing Google Safe Browsing API lookups. */
if ($phpMussel['Config']['urlscanner']['google_api_key'] && $URLScanner['URLsCount']) {
$URLScanner['URLsChunked'] = (
$URLScanner['URLsCount'] > 500
) ? array_chunk($URLScanner['URLParts'], 500) : [$URLScanner['URLParts']];
$URLScanner['URLChunks'] = count($URLScanner['URLsChunked']);
for ($i = 0; $i < $URLScanner['URLChunks']; $i++) {
if (
$phpMussel['Config']['urlscanner']['maximum_api_lookups'] > 0 &&
$phpMussel['LookupCount'] > $phpMussel['Config']['urlscanner']['maximum_api_lookups']
) {
if ($phpMussel['Config']['urlscanner']['maximum_api_lookups_response']) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$out .=
$lnap . $phpMussel['lang']['too_many_urls'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['too_many_urls'] .
' (' . $ofnSafe . ')' .
$phpMussel['lang']['_exclamation'];
}
break;
}
try {
$URLScanner['SafeBrowseLookup'] = $phpMussel['SafeBrowseLookup'](
$URLScanner['URLsChunked'][$i],
$URLScanner['URLPartsNoLookup'],
$URLScanner['DomainPartsNoLookup']
);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
if ($URLScanner['SafeBrowseLookup'] !== 204) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$URLScanner['langRef'] = 'SafeBrowseLookup_' . $URLScanner['SafeBrowseLookup'];
if (empty($phpMussel['lang'][$URLScanner['langRef']])) {
$URLScanner['langRef'] = 'SafeBrowseLookup_999';
}
$out .=
$lnap . $phpMussel['lang'][$URLScanner['langRef']] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang'][$URLScanner['langRef']] . ' (' . $ofnSafe . ')' .
$phpMussel['lang']['_exclamation'];
}
}
}
}
/** URL scanner data cleanup. */
unset($URLScanner);
/** PHP chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_from_php']) {
if (
!(
substr_count(',cvd,inc,md,phar,pzp,tpl,txt,tzt,', ',' . $xt . ',') ||
substr_count(',php*,', ',' . $xts . ',') ||
substr_count(',cvd,inc,md,phar,pzp,tpl,txt,tzt,', ',' . $gzxt . ',') ||
substr_count(',php*,', ',' . $gzxts . ',') ||
substr_count(',' . $phpMussel['Config']['attack_specific']['archive_file_extensions'] . ',', ',' . $xts . ',') ||
substr_count(',' . $phpMussel['Config']['attack_specific']['archive_file_extensions'] . ',', ',' . $gzxts . ',') ||
substr_count(',' . $phpMussel['Config']['attack_specific']['archive_file_extensions'] . ',', ',' . $xt . ',') ||
substr_count(',' . $phpMussel['Config']['attack_specific']['archive_file_extensions'] . ',', ',' . $gzxt . ',')
) &&
substr_count($str_hex_norm,'3c3f706870')
) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'PHP'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'PHP'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
/** Executable chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_from_exe']) {
if (substr_count(',acm,ax,com,cpl,dll,drv,exe,ocx,rs,scr,sys,', ',' . $xt . ',')) {
if ($twocc !== '4d5a') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'EXE'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'EXE'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
} elseif ($twocc === '4d5a') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'EXE'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'EXE'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
if ($xt === 'elf') {
if ($fourcc !== '7f454c46') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'ELF'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'ELF'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
} elseif ($fourcc === '7f454c46') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'ELF'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'ELF'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
if ($xt === 'lnk') {
if (substr($str_hex, 0, 16) !== '4c00000001140200') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'LNK'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'LNK'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
} elseif (substr($str_hex, 0, 16) === '4c00000001140200') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'LNK'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'LNK'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
if ($xt === 'msi') {
if (substr($str_hex, 0, 16) !== 'd0cf11e0a1b11ae1') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'MSI'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'MSI'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
}
/** Archive chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_to_archive']) {
if ($xts === 'zip*') {
if ($twocc !== '504b') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'ZIP'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'ZIP'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'rar') {
if ($fourcc !== '52617221' && $fourcc !== '52457e5e') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'RAR'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'RAR'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'gz') {
if ($twocc !== '1f8b') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'GZIP'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'GZIP'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'bz2') {
if (substr($str_hex,0,6) !== '425a68') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'BZIP2'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'BZIP2'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
}
/** Office document chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_to_doc']) {
if (substr_count(',doc,dot,pps,ppt,xla,xls,wiz,', ',' . $xt . ',')) {
if ($fourcc !== 'd0cf11e0') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'Office'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'Office'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
}
/** Image chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_to_img']) {
if ($xt === 'bmp' || $xt === 'dib') {
if ($twocc !== '424d') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'gif') {
if (substr($str_hex, 0, 12) !== '474946383761' && substr($str_hex, 0, 12) !== '474946383961') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if (
$xt === 'jfi' ||
$xt === 'jfif' ||
$xt === 'jif' ||
$xt === 'jpe' ||
$xt === 'jpeg' ||
$xt === 'jpg'
) {
if (substr($str_hex,0,6) !== 'ffd8ff') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'jp2') {
if (substr($str_hex, 0, 16) !== '0000000c6a502020') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'pdd' || $xt === 'psd') {
if ($fourcc !== '38425053') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'png') {
if ($fourcc !== '89504e47') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'webp') {
if ($fourcc !== '52494646' || substr($str, 8, 4) !== 'WEBP') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
if ($xt === 'xcf') {
if (substr($str,0,8) !== 'gimp xcf') {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => $phpMussel['lang']['image']],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
}
/** PDF chameleon attack detection. */
if ($phpMussel['Config']['attack_specific']['chameleon_to_pdf']) {
if ($xt === 'pdf' && !$pdf_magic) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
$out .= $lnap . $phpMussel['ParseVars'](
['x' => 'PDF'],
$phpMussel['lang']['scan_chameleon']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['x' => 'PDF'],
$phpMussel['lang']['scan_chameleon']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
/** Control character detection. */
if ($phpMussel['Config']['attack_specific']['block_control_characters']) {
if (preg_match('/[\x00-\x08\x0b\x0c\x0e\x1f\x7f]/i', $str)) {
$out .=
$lnap .
$phpMussel['lang']['detected_control_characters'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$phpMussel['whyflagged'] .=
$phpMussel['lang']['detected_control_characters'] .
' (' . $ofnSafe . ')' .
$phpMussel['lang']['_exclamation'];
}
}
/**
* If the heuristic weight of the current scan iteration exceeds the
* heuristic threshold defined by the configuration, or if outs has already
* been filled, dump all heuristic detections and non-heuristic detections
* together into outs and regard the iteration as flagged.
*/
if (
$heur['weight'] >= $phpMussel['Config']['heuristic']['threshold'] ||
$out
) {
$out .= $heur['cli'];
$phpMussel['whyflagged'] .= $heur['web'];
}
/** Virus Total API integration. */
if (
!$out &&
!empty($phpMussel['Config']['virustotal']['vt_public_api_key'])
) {
$DoScan = false;
$phpMussel['Config']['virustotal']['vt_suspicion_level'] =
(int)$phpMussel['Config']['virustotal']['vt_suspicion_level'];
if ($phpMussel['Config']['virustotal']['vt_suspicion_level'] === 0) {
$DoScan = ($heur['weight'] > 0);
} elseif ($phpMussel['Config']['virustotal']['vt_suspicion_level'] === 1) {
$DoScan = (
$heur['weight'] > 0 ||
$is_pe ||
$fileswitch === 'chrome' ||
$fileswitch === 'java' ||
$fileswitch === 'docfile' ||
$fileswitch === 'vt_interest'
);
} elseif ($phpMussel['Config']['virustotal']['vt_suspicion_level'] === 2) {
$DoScan = true;
}
if ($DoScan) {
$VTWeight = ['weight' => 0, 'cli' => '', 'web' => ''];
if (!isset($phpMussel['memCache']['vt_quota'])) {
$phpMussel['memCache']['vt_quota'] = $phpMussel['FetchCache']('vt_quota');
}
$x = 0;
if (!empty($phpMussel['memCache']['vt_quota'])) {
$phpMussel['memCache']['vt_quota'] = explode(';', $phpMussel['memCache']['vt_quota']);
foreach ($phpMussel['memCache']['vt_quota'] as &$phpMussel['ThisQuota']) {
if ($phpMussel['ThisQuota'] > $phpMussel['Time']) {
$x++;
} else {
$phpMussel['ThisQuota'] = '';
}
}
unset($phpMussel['ThisQuota']);
$phpMussel['memCache']['vt_quota'] =
implode(';', $phpMussel['memCache']['vt_quota']);
}
if ($x < $phpMussel['Config']['virustotal']['vt_quota_rate']) {
$VTParams = [
'apikey' => $phpMussel['Config']['virustotal']['vt_public_api_key'],
'resource' => $md5
];
$VTRequest = $phpMussel['Request'](
'http://www.virustotal.com/vtapi/v2/file/report?apikey=' .
urlencode($phpMussel['Config']['virustotal']['vt_public_api_key']) .
'&resource=' . $md5,
$VTParams, 12);
$VTJSON = json_decode($VTRequest, true);
$y = $phpMussel['Time'] + ($phpMussel['Config']['virustotal']['vt_quota_time'] * 60);
$phpMussel['memCache']['vt_quota'] .= $y . ';';
while (substr_count($phpMussel['memCache']['vt_quota'], ';;')) {
$phpMussel['memCache']['vt_quota'] = str_ireplace(';;', ';', $phpMussel['memCache']['vt_quota']);
}
$phpMussel['SaveCache']('vt_quota', $y + 60, $phpMussel['memCache']['vt_quota']);
if (isset($VTJSON['response_code'])) {
$VTJSON['response_code'] = (int)$VTJSON['response_code'];
if (
isset($VTJSON['scans']) &&
$VTJSON['response_code'] === 1 &&
is_array($VTJSON['scans'])
) {
foreach ($VTJSON['scans'] as $VTKey => $VTValue) {
if ($VTValue['detected'] && $VTValue['result']) {
$VN = $VTKey . '(VirusTotal)-' . $VTValue['result'];
if (
!substr_count($phpMussel['memCache']['greylist'], ',' . $VN . ',') &&
empty($phpMussel['memCache']['ignoreme'])
) {
if (!$flagged) {
$phpMussel['killdata'] .= $md5 . ':' . $str_len . ':' . $ofn . "\n";
$flagged = true;
}
$heur['detections']++;
$phpMussel['memCache']['detections_count']++;
if ($phpMussel['Config']['virustotal']['vt_weighting'] > 0) {
$VTWeight['weight']++;
$VTWeight['web'] .= $lnap . $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$VTWeight['cli'] .= $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
} else {
$out .= $lnap . $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . $phpMussel['lang']['_exclamation_final'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['ParseVars'](
['vn' => $VN],
$phpMussel['lang']['detected']
) . ' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
}
}
}
}
}
}
if (
$VTWeight['weight'] > 0 &&
$VTWeight['weight'] >= $phpMussel['Config']['virustotal']['vt_weighting']
) {
$out .= $VTWeight['web'];
$phpMussel['whyflagged'] .= $VTWeight['cli'];
}
}
}
}
/** Plugin hook: "after_vt". */
$phpMussel['Execute_Hook']('after_vt');
if (
isset($phpMussel['HashCacheData']) &&
!isset($phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']]) &&
$phpMussel['Config']['general']['scan_cache_expiry'] > 0
) {
if (empty($phpMussel['HashCache']['Data']) || !is_array($phpMussel['HashCache']['Data'])) {
$phpMussel['HashCache']['Data'] = [];
}
$phpMussel['HashCache']['Data'][$phpMussel['HashCacheData']] = [
$phpMussel['HashCacheData'],
$phpMussel['Time'] + $phpMussel['Config']['general']['scan_cache_expiry'],
(empty($out) ? '' : bin2hex($out)),
(empty($phpMussel['whyflagged']) ? '' : bin2hex($phpMussel['whyflagged']))
];
}
/** Set final debug values, if this has been enabled. */
if (isset($phpMussel['DebugArr']) && isset($phpMussel['DebugArrKey'])) {
$phpMussel['DebugArr'][$phpMussel['DebugArrKey']]['Results'] = !$out ? 1 : 2;
$phpMussel['DebugArr'][$phpMussel['DebugArrKey']]['Output'] = $out;
}
if ($out) {
/** Register object flagged. */
if (isset($phpMussel['cli_args'][1]) && $phpMussel['cli_args'][1] == 'cli_scan') {
$phpMussel['Stats-Increment']('CLI-Flagged', 1);
} else {
$phpMussel['Stats-Increment']($phpMussel['EOF'] ? 'API-Flagged' : 'Web-Blocked', 1);
}
}
/** Exit data handler. */
return !$out ? [1, ''] : [2, $out];
};
/**
* Handles scanning for files contained within archives.
*
* @param string $ItemRef A reference to the path and original filename of the
* item being scanned in relation to its container and/or its heirarchy
* within the scan process.
* @param string $Filename The original filename of the item being scanned.
* @param string $Data The data to be scanned.
* @param int $Depth The depth of the item being scanned in relation to its
* container and/or its heirarchy within the scan process.
* @param string $lnap Line padding for the scan results.
* @param int $r Scan results inherited from parent in the form of an integer.
* @param string $x Scan results inherited from parent in the form of a string.
* @return array Returns an array containing the results of the scan as both an
* integer (the first element) and as human-readable text (the second
* element).
*/
$phpMussel['MetaDataScan'] = function ($ItemRef, $Filename, $Data, $Depth, $lnap, $r, $x) use (&$phpMussel) {
if (!$Filesize = strlen($Data)) {
return [$r, $x];
}
$ItemRefSafe = urlencode($ItemRef);
$MD5 = md5($Data);
if (
$phpMussel['Config']['files']['filesize_archives'] &&
$phpMussel['Config']['files']['filesize_limit'] > 0 &&
$Filesize > $phpMussel['ReadBytes']($phpMussel['Config']['files']['filesize_limit'])
) {
if (!$phpMussel['Config']['files']['filesize_response']) {
$x .=
$lnap . $phpMussel['lang']['ok'] . ' (' .
$phpMussel['lang']['filesize_limit_exceeded'] . ").\n";
return [$r, $x];
}
$r = 2;
$phpMussel['killdata'] .= $MD5 . ':' . $Filesize . ':' . $ItemRef . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filesize_limit_exceeded'] .
' (' . $ItemRefSafe . ')' .
$phpMussel['lang']['_exclamation'];
$x .=
$lnap . $phpMussel['lang']['filesize_limit_exceeded'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
return [$r, $x];
}
if (
substr($Filename, 0, 1) === '.' ||
substr($Filename, -1) === '.'
) {
$r = 2;
$phpMussel['killdata'] .= $MD5 . ':' . $Filesize . ':' . $ItemRef . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_filename_manipulation_detected'] .
' (' . $ItemRefSafe . ')' .
$phpMussel['lang']['_exclamation'];
$x .=
$lnap . $phpMussel['lang']['scan_filename_manipulation_detected'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
return [$r, $x];
}
if ($phpMussel['Config']['files']['filetype_archives']) {
$decPos = strrpos($Filename, '.');
$ofnLen = strlen($Filename);
if ($decPos === false || $decPos === ($ofnLen - 1)) {
$xts = $xt = '-';
} else {
$xt = strtolower(substr($Filename, ($decPos + 1)));
$xts = substr($xt, 0, 3) . '*';
}
if (
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $xt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $xts . ',')
) {
$x .= $lnap . $phpMussel['lang']['scan_no_problems_found'] . "\n";
return [$r, $x];
}
if (
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $xt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $xts . ',')
) {
$r = 2;
$phpMussel['killdata'] .= $MD5 . ':' . $Filesize . ':' . $ItemRef . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filetype_blacklisted'] .
' (' . $ItemRefSafe . ')' .
$phpMussel['lang']['_exclamation'];
$x .=
$lnap . $phpMussel['lang']['filetype_blacklisted'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
return [$r, $x];
}
if (
$phpMussel['Config']['files']['filetype_greylist'] &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $xt . ',') &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $xts . ',')
) {
$r = 2;
$phpMussel['killdata'] .= $MD5 . ':' . $Filesize . ':' . $ItemRef . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filetype_blacklisted'] .
' (' . $ItemRefSafe . ')' .
$phpMussel['lang']['_exclamation'];
$x .=
$lnap . $phpMussel['lang']['filetype_blacklisted'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
return [$r, $x];
}
}
$phpMussel['memCache']['objects_scanned']++;
try {
$Scan = $phpMussel['DataHandler']($Data, $Depth, $Filename);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
if ($Scan[0] !== 1) {
return [$Scan[0], $x . $Scan[1]];
}
return [$r, $x];
};
/**
* Looks for indicators of image files (i.e., attempts to determine whether a
* file is an image file).
*
* @param string $Ext The file extension.
* @param string $Head The file header.
* @return bool True: Indicators found. False: Indicators not found.
*/
$phpMussel['Indicator-Image'] = function ($Ext, $Head) {
return (
preg_match(
'/^(?:bm[2p]|c(d5|gm)|d(ib|w[fg]|xf)|ecw|fits|gif|img|j(f?if?|p[2s]|pe?g?2?|xr)|p(bm|cx|dd|gm|ic|n[gms]|' .
'pm|s[dp])|s(id|v[ag])|tga|w(bmp?|ebp|mp)|x(cf|bmp))$/'
, $Ext) ||
preg_match(
'/^(?:0000000c6a502020|25504446|38425053|424d|474946383[79]61|57454250|67696d7020786366|89504e47|ffd8ff)/'
, $Head)
);
};
/**
* Fetches extensions data from filenames.
*
* @param string $ofn The original filename.
* @return array The extensions data.
*/
$phpMussel['FetchExt'] = function ($ofn) {
$decPos = strrpos($ofn, '.');
$ofnLen = strlen($ofn);
if ($decPos === false || $decPos === ($ofnLen - 1)) {
return ['-', '-', '-', '-'];
}
$xt = strtolower(substr($ofn, ($decPos + 1)));
$xts = substr($xt, 0, 3) . '*';
if (strtolower(substr($ofn, -3)) === '.gz') {
$ofnNoGZ = substr($ofn, 0, ($ofnLen - 3));
$decPosNoGZ = strrpos($ofnNoGZ, '.');
if ($decPosNoGZ !== false && $decPosNoGZ !== (strlen($ofnNoGZ) - 1)) {
$gzxt = strtolower(substr($ofnNoGZ, ($decPosNoGZ + 1)));
$gzxts = substr($gzxt, 0, 3) . '*';
}
} else {
$gzxts = $gzxt = '-';
}
return [$xt, $xts, $gzxt, $gzxts];
};
/**
* Responsible for recursing through any files given to it to be scanned, which
* may be necessary for the case of archives and directories. It performs the
* preparations necessary for scanning files using the "data handler" and the
* "meta data scan" closures. Additionally, it performs some necessary
* whitelist, blacklist and greylist checks, filesize and file extension
* checks, and handles the processing and extraction of files from archives,
* fetching the files contained in archives being scanned in order to process
* those contained files as so that they, too, may be scanned.
*
* When phpMussel is instructed to scan a directory or an array of multiple
* files, the recursor is the closure function responsible for iterating
* through that directory and/or array queued for scanning, and if necessary,
* will recurse itself (such as for when scanning a directory containing
* sub-directories or when scanning a multidimensional array of multiple files
* and/or directories).
*
* @param string|array $f In the context of the initial file upload scanning
* that phpMussel performs when operating via a server, this parameter (a
* string) represents the "temporary filename" of the file being scanned
* (the temporary filename, in this context, referring to the name
* temporarily assigned to the file by the server upon the file being
* uploaded to the temporary uploads location assigned to the server).
* When operating in the context of CLI mode, both $f and $ofn represent
* the scan target, as per specified by the CLI operator; The only
* difference between the two is when the scan target is a directory,
* rather than a single file; $f will represent the full path to the file
* (so, directory plus filename), whereas $ofn will represent only the
* filename. This parameter can also accept an array of filenames.
* @param bool $n This optional parameter is a boolean (defaults to false, but
* set to true during the initial scan of file uploads), indicating the
* format for returning the scan results. False instructs the function to
* return results as an integer; True instructs the function to return
* results as human readable text (refer to Section 3A of the README
* documentation, "HOW TO USE (FOR WEB SERVERS)", for more information).
* @param bool $zz This optional parameter is a boolean (defaults to false, but
* set to true during the initial scan of file uploads), indicating to the
* function whether or not arrayed results should be imploded prior to
* being returned to the calling function. False instructs the function to
* return the arrayed results as verbatim; True instructs the function to
* return the arrayed results as an imploded string.
* @param int $dpt Represents the current depth of recursion from which the
* function has been called. This information is used for determining how
* far to indent any entries generated for logging and for the display of
* scan results in CLI.
* @param string $ofn In the context of the initial file upload scanning that
* phpMussel performs when operating via a server, this parameter (a
* string) represents the "temporary filename" of the file being scanned
* (the temporary filename, in this context, referring to the name
* temporarily assigned to the file by the server upon the file being
* uploaded to the temporary uploads location assigned to the server).
* When operating in the context of CLI mode, both $f and $ofn represent
* the scan target, as per specified by the CLI operator; The only
* difference between the two is when the scan target is a directory,
* rather than a single file; $f will represent the full path to the file
* (so, directory plus filename), whereas $ofn will represent only the
* filename.
* @return int|string|array The scan results, returned as an array when the $f
* parameter is an array and when $n and/or $zz is/are false, and
* otherwise returned as per described by the README documentation. The
* function may also die the script and return nothing, if something goes
* wrong, such as if the function is triggered in the absense of the
* required $phpMussel['memCache'] variable being set.
*/
$phpMussel['Recursor'] = function ($f = '', $n = false, $zz = false, $dpt = 0, $ofn = '') use (&$phpMussel) {
if (!isset($phpMussel['memCache'])) {
throw new \Exception(
(!isset($phpMussel['lang']['required_variables_not_defined'])) ?
'[phpMussel] Required variables aren\'t defined: Can\'t continue.' :
'[phpMussel] ' . $phpMussel['lang']['required_variables_not_defined']
);
}
/** Prepare signature files for the scan process. */
if (empty($phpMussel['memCache']['OrganisedSigFiles'])) {
$phpMussel['OrganiseSigFiles']();
$phpMussel['memCache']['OrganisedSigFiles'] = true;
}
if ($phpMussel['EOF']) {
$phpMussel['whyflagged'] = $phpMussel['killdata'] = $phpMussel['PEData'] = '';
if (
$dpt === 0 ||
!isset($phpMussel['memCache']['objects_scanned']) ||
!isset($phpMussel['memCache']['detections_count']) ||
!isset($phpMussel['memCache']['scan_errors'])
) {
$phpMussel['memCache']['objects_scanned'] =
$phpMussel['memCache']['detections_count'] =
$phpMussel['memCache']['scan_errors'] = 0;
}
} else {
if (!isset($phpMussel['killdata'])) {
$phpMussel['killdata'] = '';
}
if (!isset($phpMussel['whyflagged'])) {
$phpMussel['whyflagged'] = '';
}
if (!isset($phpMussel['PEData'])) {
$phpMussel['PEData'] = '';
}
if (
!isset($phpMussel['memCache']['objects_scanned']) ||
!isset($phpMussel['memCache']['detections_count']) ||
!isset($phpMussel['memCache']['scan_errors'])
) {
$phpMussel['memCache']['objects_scanned'] =
$phpMussel['memCache']['detections_count'] =
$phpMussel['memCache']['scan_errors'] = 0;
}
}
/** Increment scan depth. */
$dpt++;
/** Controls indenting relating to scan depth for normal logging and for CLI-mode scanning. */
$lnap = str_pad('> ', ($dpt + 1), '-', STR_PAD_LEFT);
/**
* If the scan target is an array, iterate through the array and recurse
* the recursor with each array element.
*/
if (is_array($f)) {
foreach ($f as &$Current) {
try {
$Current = $phpMussel['Recursor']($Current, $n, false, $dpt, $Current);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
}
return ($n && $zz) ? $phpMussel['implode_md']($f) : $f;
}
$ofn = $phpMussel['prescan_decode']($ofn);
$ofnSafe = urlencode($ofn);
/**
* If the scan target is a directory, iterate through the directory
* contents and recurse the recursor with these contents.
*/
if (is_dir($f)) {
if (!is_readable($f)) {
$phpMussel['memCache']['scan_errors']++;
return !$n ? 0 :
$lnap . $phpMussel['lang']['failed_to_access'] . '\'' . $ofn . '\'' .
$phpMussel['lang']['_exclamation_final'] . "\n";
}
$Dir = $phpMussel['DirectoryRecursiveList']($f);
foreach ($Dir as &$Sub) {
try {
$Sub = $phpMussel['Recursor']($f . '/' . $Sub, $n, false, $dpt, $Sub);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
}
return ($n && $zz) ? $phpMussel['implode_md']($Dir) : $Dir;
}
/** Increment our scanned files/objects tally. */
$phpMussel['memCache']['objects_scanned']++;
/** Define file phase. */
$phpMussel['memCache']['phase'] = 'file';
/**
* Indicates whether the file/object being scanned is a part of a
* container (e.g., an OLE object, ZIP file, TAR, PHAR, etc).
*/
$phpMussel['memCache']['container'] = 'none';
/** Indicates whether the file/object being scanned is an OLE object. */
$phpMussel['memCache']['file_is_ole'] = false;
/** Fetch the greylist if it hasn't already been fetched. */
if (!isset($phpMussel['memCache']['greylist'])) {
if (!file_exists($phpMussel['Vault'] . 'greylist.csv')) {
$phpMussel['memCache']['greylist'] = ',';
$Handle = fopen($phpMussel['Vault'] . 'greylist.csv', 'a');
fwrite($Handle, ',');
fclose($Handle);
} else {
$phpMussel['memCache']['greylist'] = $phpMussel['ReadFile']($phpMussel['Vault'] . 'greylist.csv');
}
}
/** Plugin hook: "before_scan". */
$phpMussel['Execute_Hook']('before_scan');
$fnCRC = hash('crc32b', $ofn);
/** Kill it here if the scan target isn't a valid file. */
if (!$f || !$d = is_file($f)) {
return (!$n) ? 0 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' . $ofn .
'\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['invalid_file'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
}
$fS = filesize($f);
if ($phpMussel['Config']['files']['filesize_limit'] > 0) {
if ($fS > $phpMussel['ReadBytes']($phpMussel['Config']['files']['filesize_limit'])) {
if (!$phpMussel['Config']['files']['filesize_response']) {
return (!$n) ? 1 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' .
$ofn . '\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['ok'] . ' (' .
$phpMussel['lang']['filesize_limit_exceeded'] . ").\n";
}
$phpMussel['killdata'] .=
'--FILESIZE-LIMIT--------NO-HASH-:' . $fS . ':' . $ofn . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filesize_limit_exceeded'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? 2 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' . $ofn .
'\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['filesize_limit_exceeded'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
}
}
if (substr($ofn, 0, 1) === '.' || substr($ofn, -1) === '.') {
$phpMussel['killdata'] .=
'--FILENAME-MANIPULATION-NO-HASH-:' . $fS . ':' . $ofn . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_filename_manipulation_detected'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? 2 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' . $ofn .
'\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['scan_filename_manipulation_detected'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
}
list($xt, $xts, $gzxt, $gzxts) = $phpMussel['FetchExt']($ofn);
if (
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $xt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $xts . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $gzxt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_whitelist'] . ',', ',' . $gzxts . ',')
) {
return (!$n) ? 1 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' . $ofn .
'\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['scan_no_problems_found'] . "\n";
}
if (
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $xt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $xts . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $gzxt . ',') ||
substr_count(',' . $phpMussel['Config']['files']['filetype_blacklist'] . ',', ',' . $gzxts . ',')
) {
$phpMussel['killdata'] .=
'--FILETYPE-BLACKLISTED--NO-HASH-:' . $fS . ':' . $ofn . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filetype_blacklisted'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? 2 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' .
$ofn . '\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['filetype_blacklisted'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
}
if (
$phpMussel['Config']['files']['filetype_greylist'] &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $xt . ',') &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $xts . ',') &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $gzxt . ',') &&
!substr_count(',' . $phpMussel['Config']['files']['filetype_greylist'] . ',', ',' . $gzxts . ',')
) {
$phpMussel['killdata'] .=
'----FILETYPE--NOT-GREYLISTED----:' . $fS . ':' . $ofn . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['filetype_blacklisted'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? 2 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' .
$ofn . '\' (FN: ' . $fnCRC . "):\n-" . $lnap .
$phpMussel['lang']['filetype_blacklisted'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
}
$in = $phpMussel['ReadFile']($f, (
$phpMussel['Config']['attack_specific']['scannable_threshold'] > 0 &&
$fS > $phpMussel['ReadBytes']($phpMussel['Config']['attack_specific']['scannable_threshold'])
) ? $phpMussel['ReadBytes']($phpMussel['Config']['attack_specific']['scannable_threshold']) : $fS, true);
$fdCRC = hash('crc32b', $in);
/** Check for non-image items. */
if (!empty($in) && $phpMussel['Config']['compatibility']['only_allow_images'] && !$phpMussel['Indicator-Image']($xt, bin2hex(substr($in, 0, 16)))) {
$phpMussel['killdata'] .= md5($in) . ':' . $fS . ':' . $ofn . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['only_allow_images'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? 2 :
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' .
$ofn . '\' (FN: ' . $fnCRC . '; FD: ' . $fdCRC . "):\n-" .
$lnap . $phpMussel['lang']['only_allow_images'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
}
/** Send the file/object being scanned to the data handler. */
try {
$z = $phpMussel['DataHandler']($in, $dpt, $ofn);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
/** Executed if there were any problems or anything detected: */
if ($z[0] !== 1) {
if ($z[0] === 2) {
if (
$phpMussel['Config']['general']['quarantine_key'] &&
!$phpMussel['Config']['general']['honeypot_mode'] &&
strlen($in) < $phpMussel['ReadBytes']($phpMussel['Config']['general']['quarantine_max_filesize'])
) {
$qfu =
$phpMussel['Time'] .
'-' .
md5($phpMussel['Config']['general']['quarantine_key'] . $fdCRC . $phpMussel['Time']);
$phpMussel['Quarantine'](
$in,
$phpMussel['Config']['general']['quarantine_key'],
$_SERVER[$phpMussel['Config']['general']['ipaddr']],
$qfu
);
$phpMussel['killdata'] .= $phpMussel['ParseVars'](
['QFU' => $qfu],
$phpMussel['lang']['quarantined_as']
);
}
}
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return (!$n) ? $z[0] :
$lnap . $phpMussel['lang']['scan_checking'] .
' \'' . $ofn . '\' (FN: ' . $fnCRC . '; FD: ' . $fdCRC . "):\n" .
$z[1];
}
$x =
$lnap . $phpMussel['lang']['scan_checking'] . ' \'' .
$ofn . '\' (FN: ' . $fnCRC . '; FD: ' . $fdCRC . "):\n-" . $lnap .
$phpMussel['lang']['scan_no_problems_found'] . "\n";
$r = 1;
/**
* Begin archive phase.
* Note: Archive phase will only occur when "check_archives" is enabled and
* when no problems were detected with the file/object being scanned by
* this stage of the scan.
*/
if (
$phpMussel['Config']['files']['check_archives'] &&
!empty($in) &&
$phpMussel['Config']['files']['max_recursion'] > 1
) {
/** Define archive phase. */
$phpMussel['memCache']['phase'] = 'archive';
/** Reset container definition. */
$phpMussel['memCache']['container'] = 'none';
/** Set appropriate container definitions. */
if (substr($in, 0, 2) === 'PK') {
if ($xt === 'ole') {
$PharType = 'OLE';
} elseif ($xt === 'smpk') {
$PharType = 'SMPTE';
} elseif ($xt === 'xpi') {
$PharType = 'XPInstall';
} elseif ($xts === 'app*') {
$PharType = 'App';
} elseif (substr_count(
',docm,docx,dotm,dotx,potm,potx,ppam,ppsm,ppsx,pptm,pptx,xlam,xlsb,xlsm,x' .
'lsx,xltm,xltx,', ',' . $xt . ','
)) {
$PharType = 'OpenXML';
} elseif (substr_count(
',odc,odf,odg,odm,odp,ods,odt,otg,oth,otp,ots,ott,', ',' . $xt . ','
) || $xts === 'fod*') {
$PharType = 'OpenDocument';
} elseif (substr_count(',opf,epub,', ',' . $xt . ',')) {
$PharType = 'EPUB';
} else {
$PharType = 'ZIP';
$phpMussel['memCache']['container'] = 'zipfile';
}
if ($PharType !== 'ZIP') {
$phpMussel['memCache']['file_is_ole'] = true;
$phpMussel['memCache']['container'] = 'pkfile';
}
} elseif (
substr($in, 257, 6) === "ustar\x00" ||
substr_count(',tar,tgz,tbz,tlz,tz,', ',' . $xt . ',')
) {
$PharType = 'TarFile';
$phpMussel['memCache']['container'] = 'tarfile';
} elseif (
substr($in, 0, 4) === 'Rar!' ||
bin2hex(substr($in, 0, 4)) === '52457e5e'
) {
$PharType = 'RarFile';
$phpMussel['memCache']['container'] = 'rarfile';
} else {
$PharType = '';
}
/** Check if PHARable, and if so, generate an array of the contents. */
if (is_dir('phar://' . $f) && is_readable('phar://' . $f)) {
$x .=
'-' . $lnap . $phpMussel['lang']['scan_reading'] .
' \'' . $ofn . "' (PHAR):\n";
$PharData = $phpMussel['BuildPharList']($f, $dpt);
$PharData = explode("\n", $PharData);
$PharCount = count($PharData);
/** Iterate through each item in the PHARable file/object. */
for ($PharIter = 0; $PharIter < $PharCount; $PharIter++) {
if (empty($PharData[$PharIter])) {
continue;
}
$PharData[$PharIter] = [
'DoScan' => true,
'Depth' => $phpMussel['substrbf']($PharData[$PharIter], ' '),
'Path' => $phpMussel['substraf']($PharData[$PharIter], ' ')
];
$PharData[$PharIter]['Data'] = $phpMussel['ReadFile']('phar://' . $PharData[$PharIter]['Path']);
$PharData[$PharIter]['Filename'] =
(substr_count($PharData[$PharIter]['Path'], '/')) ?
$phpMussel['substral']($PharData[$PharIter]['Path'], '/') :
$PharData[$PharIter]['Path'];
$PharData[$PharIter]['ItemRef'] = $ofn . '>' . $PharData[$PharIter]['Path'];
}
} else {
/** Default to the parent if the parent isn't PHARable. */
$PharData = [0 => ['DoScan' => false, 'Depth' => 1, 'Path' => $f, 'Data' => $in]];
$PharData[0]['Filename'] = (substr_count($f, '/')) ? $phpMussel['substral']($f, '/') : $f;
$PharData[0]['ItemRef'] = $ofn . '>' . $PharData[0]['Path'];
$PharCount = 1;
}
/** And now we begin processing our PHARable contents array. */
for ($PharIter = 0; $PharIter < $PharCount; $PharIter++) {
if (empty($PharData[$PharIter])) {
continue;
}
$PharData[$PharIter]['Filesize'] = strlen($PharData[$PharIter]['Data']);
$PharData[$PharIter]['MD5'] = md5($PharData[$PharIter]['Data']);
$PharData[$PharIter]['NameCRC32'] = hash('crc32b', $PharData[$PharIter]['Filename']);
$PharData[$PharIter]['DataCRC32'] = hash('crc32b', $PharData[$PharIter]['Data']);
if (
$phpMussel['Config']['files']['block_encrypted_archives'] &&
substr($PharData[$PharIter]['Data'], 0, 2) === 'PK'
) {
/**
* ZIP File Format Specification:
* - https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
*/
$PharData[$PharIter]['ZipBits'] =
$phpMussel['explode_bits'](substr($PharData[$PharIter]['Data'], 6, 2));
if ($PharData[$PharIter]['ZipBits'] && $PharData[$PharIter]['ZipBits'][7]) {
/** Encryption detected. */
$r = 2;
$phpMussel['killdata'] .=
$PharData[$PharIter]['MD5'] . ':' .
$PharData[$PharIter]['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['encrypted_archive'] .
' (' . urlencode($PharData[$PharIter]['ItemRef']) . ')' .
$phpMussel['lang']['_exclamation'];
$x .=
'-' . $lnap . $phpMussel['lang']['scan_checking'] .
' \'' . $PharData[$PharIter]['ItemRef'] . '\' (FN: ' .
$PharData[$PharIter]['NameCRC32'] . '; FD: ' .
$PharData[$PharIter]['DataCRC32'] . "):\n--" . $lnap .
$phpMussel['lang']['encrypted_archive'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
break;
}
}
if ($PharData[$PharIter]['DoScan']) {
$x .=
'-' . $lnap . $phpMussel['lang']['scan_checking'] .
' \'' . $PharData[$PharIter]['ItemRef'] .
'\' (FN: ' . $PharData[$PharIter]['NameCRC32'] .
'; FD: ' . $PharData[$PharIter]['DataCRC32'] . "):\n";
try {
list($r, $x) = $phpMussel['MetaDataScan'](
$PharData[$PharIter]['ItemRef'],
$PharData[$PharIter]['Filename'],
$PharData[$PharIter]['Data'],
$PharData[$PharIter]['Depth'],
'-' . $lnap,
$r,
$x
);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
if ($r !== 1) {
break;
}
$x .= '--' . $lnap . $phpMussel['lang']['scan_no_problems_found'] . "\n";
}
$ScanDepth = 0;
while (true) {
if ($ScanDepth > $phpMussel['Config']['files']['max_recursion']) {
$r = 2;
$phpMussel['killdata'] .=
$PharData[$PharIter]['MD5'] . ':' .
$PharData[$PharIter]['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['recursive'] .
' (' . $ofnSafe . ')' . $phpMussel['lang']['_exclamation'];
$x .=
'-' . $lnap . $phpMussel['lang']['scan_checking'] .
' \'' . $PharData[$PharIter]['ItemRef'] . '\' (FN: ' .
$PharData[$PharIter]['NameCRC32'] . '; FD: ' .
$PharData[$PharIter]['DataCRC32'] . "):\n--" .
$lnap . $phpMussel['lang']['recursive'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
break 2;
}
$zDo = false;
$PharData[$PharIter]['Indent'] = str_repeat('-', $ScanDepth + $dpt) . $lnap;
$xtt = (strpos($PharData[$PharIter]['Filename'], '.') !== false) ? [
substr($PharData[$PharIter]['Filename'], -3),
substr($PharData[$PharIter]['Filename'], -4),
substr($PharData[$PharIter]['Filename'], -7, 4),
substr($PharData[$PharIter]['Filename'], -8, 4)
] : false;
$GZable = false;
$BZable = false;
$LZable = false;
if ($phpMussel['substr_compare_hex']($PharData[$PharIter]['Data'], 0, 2, '1f8b', true)) {
$GZable = true;
} elseif ($phpMussel['substr_compare_hex']($PharData[$PharIter]['Data'], 0, 3, '425a68', true)) {
$BZable = true;
} elseif (!empty($xtt) && !$zDo && (
substr_count(',.gz,', ',' . $xtt[0] . ',') ||
substr_count(',.tgz,', ',' . $xtt[1] . ',')
)) {
$GZable = true;
} elseif (!empty($xtt) && !$zDo && (
substr_count(',.bz,', ',' . $xtt[0] . ',') ||
substr_count(',.bz2,.tbz,', ',' . $xtt[1] . ',')
)) {
$BZable = true;
} elseif (!empty($xtt) && !$zDo && (
substr_count(',.lz,', ',' . $xtt[0] . ',') ||
substr_count(',.lha,.lzh,.lzo,.lzw,.lzx,.tlz,', ',' . $xtt[1] . ',')
)) {
$LZable = true;
} elseif (!$zDo && (
substr($PharData[$PharIter]['Data'], 257, 6) === "ustar\x00" || (
!empty($xtt) && (
substr_count(',.tar,.tgz,.tbz,.tlz,', ',' . $xtt[1] . ',') ||
substr_count(',.tar,.tgz,.tbz,.tlz,', ',' . $xtt[2] . ',') ||
substr_count(',.tar,.tgz,.tbz,.tlz,', ',' . $xtt[3] . ',')
)
)
)) {
/** Allows recursion for TAR files. */
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (TAR):\n";
$TarFile = ['Offset' => 0];
while (true) {
if (($TarFile['Offset'] + 1024) > $PharData[$PharIter]['Filesize']) {
break;
}
$TarFile['File'] = [
'Filename' => preg_replace('/[^\x20-\xff]/', '', substr($PharData[$PharIter]['Data'], $TarFile['Offset'], 100)),
'Filesize' => octdec(preg_replace('/[^0-9]/', '', substr($PharData[$PharIter]['Data'], $TarFile['Offset'] + 124, 12))),
];
if ($TarFile['File']['Filesize'] < 0) {
$r = 2;
$phpMussel['killdata'] .=
$TarFile['File']['MD5'] . ':' .
$TarFile['File']['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_tampering'] . ' (' .
urlencode($PharData[$PharIter]['ItemRef']) .
')' . $phpMussel['lang']['_exclamation'];
$x .=
'-' . $PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_tampering'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
break;
}
$TarFile['File']['Directory'] = (
substr($TarFile['File']['Filename'], -1, 1) === '/' &&
$TarFile['File']['Filesize'] === 0
);
$TarFile['File']['Blocks'] = ceil($TarFile['File']['Filesize'] / 512) + 1;
if ($TarFile['File']['Directory']) {
$TarFile['Offset'] += $TarFile['File']['Blocks'] * 512;
continue;
}
if ($TarFile['File']['Filename']) {
if (substr_count($TarFile['File']['Filename'], "\\")) {
$TarFile['File']['Filename'] =
$phpMussel['substral']($TarFile['File']['Filename'], "\\") ;
}
if (substr_count($TarFile['File']['Filename'], '/')) {
$TarFile['File']['Filename'] =
$phpMussel['substral']($TarFile['File']['Filename'], '/') ;
}
}
$TarFile['File']['Data'] =
substr($PharData[$PharIter]['Data'], $TarFile['Offset'] + 512, $TarFile['File']['Filesize']);
if (empty($TarFile['File']['Data'])) {
break;
}
$TarFile['File']['MD5'] = md5($TarFile['File']['Data']);
if (!$TarFile['File']['Filename']) {
$r = 2;
$phpMussel['killdata'] .=
$TarFile['File']['MD5'] . ':' .
$TarFile['File']['Filesize'] .
":MISSING-FILENAME\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['scan_missing_filename'] .
$phpMussel['lang']['_exclamation'];
$x .=
'-' . $PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_missing_filename'] .
$phpMussel['lang']['_exclamation_final'] . "\n";
break;
}
$phpMussel['memCache']['objects_scanned']++;
try {
list($r, $x) = $phpMussel['MetaDataScan'](
$PharData[$PharIter]['ItemRef'] . '>' . $TarFile['File']['Filename'],
$TarFile['File']['Filename'],
$TarFile['File']['Data'],
$dpt,
$PharData[$PharIter]['Indent'],
$r,
$x
);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
$TarFile['File']['DataCRC32'] = hash('crc32b', $TarFile['File']['Data']);
$TarFile['File']['NameCRC32'] = hash('crc32b', $TarFile['File']['Filename']);
$TarFile['Offset'] += $TarFile['File']['Blocks'] * 512;
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_checking'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . '>' . $TarFile['File']['Filename'] .
'\' (FN: ' . $TarFile['File']['NameCRC32'] . '; FD: ' .
$TarFile['File']['DataCRC32'] . "):\n";
if ($r === 1) {
$x .=
'-' . $PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_no_problems_found'] . "\n";
}
}
$TarFile = '';
}
if ($GZable) {
if (!function_exists('gzdecode')) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_extensions_silently']) {
$r = -1;
$phpMussel['killdata'] .=
$PharData[$PharIter]['MD5'] . ':' .
$PharData[$PharIter]['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['lang']['scan_extensions_missing'] . ' ';
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (GZIP):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_extensions_missing'] . "\n";
break;
}
if (!$PharData[$PharIter]['Data'] = gzdecode($PharData[$PharIter]['Data'])) {
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (GZIP):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_not_archive'] . "\n";
break;
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (GZIP):\n";
$zDo = true;
} elseif ($BZable) {
if (!function_exists('bzdecompress')) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_extensions_silently']) {
$r = -1;
$phpMussel['killdata'] .=
$PharData[$PharIter]['MD5'] . ':' .
$PharData[$PharIter]['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['lang']['scan_extensions_missing'] . ' ';
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (BZIP2):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_extensions_missing'] . "\n";
break;
}
if (!$PharData[$PharIter]['Data'] = bzdecompress($PharData[$PharIter]['Data'])) {
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (BZIP2):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_not_archive'] . "\n";
break;
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (BZIP2):\n";
$zDo = true;
} elseif ($LZable) {
if (!function_exists('lzf_decompress')) {
$phpMussel['memCache']['scan_errors']++;
if (!$phpMussel['Config']['signatures']['fail_extensions_silently']) {
$r = -1;
$phpMussel['killdata'] .=
$PharData[$PharIter]['MD5'] . ':' .
$PharData[$PharIter]['Filesize'] . ':' .
$PharData[$PharIter]['ItemRef'] . "\n";
$phpMussel['whyflagged'] .= $phpMussel['lang']['scan_extensions_missing'] . ' ';
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (LZF):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_extensions_missing'] . "\n";
break;
}
if (!$PharData[$PharIter]['Data'] = lzf_decompress($PharData[$PharIter]['Data'])) {
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (LZF):\n-" .
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_not_archive'] . "\n";
break;
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_reading'] . ' \'' .
$PharData[$PharIter]['ItemRef'] . "' (LZF):\n";
$zDo = true;
}
if ($zDo) {
$ScanDepth++;
if (substr_count($PharData[$PharIter]['Filename'], '.')) {
$PharData[$PharIter]['Filename'] = $phpMussel['substrbl']($PharData[$PharIter]['Filename'], '.');
}
if (substr_count($PharData[$PharIter]['Filename'], "\\")) {
$PharData[$PharIter]['Filename'] = $phpMussel['substral']($PharData[$PharIter]['Filename'], "\\");
}
if (substr_count($PharData[$PharIter]['Filename'], '/')) {
$PharData[$PharIter]['Filename'] = $phpMussel['substral']($PharData[$PharIter]['Filename'], '/');
}
$PharData[$PharIter]['Filesize'] = strlen($PharData[$PharIter]['Data']);
$PharData[$PharIter]['ItemRef'] .= '>' . $PharData[$PharIter]['Filename'];
$PharData[$PharIter]['NameCRC32'] = hash('crc32b', $PharData[$PharIter]['Filename']);
$PharData[$PharIter]['DataCRC32'] = hash('crc32b', $PharData[$PharIter]['Data']);
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_checking'] .
' \'' . $PharData[$PharIter]['ItemRef'] .
'\' (FN: ' . $PharData[$PharIter]['NameCRC32'] .
'; FD: ' . $PharData[$PharIter]['DataCRC32'] . "):\n";
try {
list($r, $x) = $phpMussel['MetaDataScan'](
$PharData[$PharIter]['ItemRef'],
$PharData[$PharIter]['Filename'],
$PharData[$PharIter]['Data'],
$PharData[$PharIter]['Depth'] + $ScanDepth + $dpt,
$PharData[$PharIter]['Indent'],
$r,
$x
);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
if ($r !== 1) {
break 2;
}
$x .=
$PharData[$PharIter]['Indent'] .
$phpMussel['lang']['scan_no_problems_found'] .
"\n";
continue;
}
break;
}
}
}
if ($r === 2) {
if (
$phpMussel['Config']['general']['quarantine_key'] &&
!$phpMussel['Config']['general']['honeypot_mode'] &&
strlen($in) < $phpMussel['ReadBytes']($phpMussel['Config']['general']['quarantine_max_filesize'])
) {
$qfu = $phpMussel['Time'] . '-' . md5(
$phpMussel['Config']['general']['quarantine_key'] . $fdCRC . $phpMussel['Time']
);
$phpMussel['Quarantine'](
$in,
$phpMussel['Config']['general']['quarantine_key'],
$_SERVER[$phpMussel['Config']['general']['ipaddr']],
$qfu
);
$phpMussel['killdata'] .= $phpMussel['ParseVars'](
['QFU' => $qfu],
$phpMussel['lang']['quarantined_as']
);
}
}
if ($r !== 1 && $phpMussel['Config']['general']['delete_on_sight'] && is_readable($f)) {
unlink($f);
}
return !$n ? $r : $x;
};
/**
* Forks the PHP process when scanning in CLI mode. This ensures that if PHP
* crashes during scanning, phpMussel can continue to scan any remaining items
* queued for scanning (because if the parent process handles the scan queue
* and the child process handles the actual scanning of each item queued for
* scanning, if the child process crashes, the parent process can simply create
* a new child process to continue iterating through the queue).
*
* There are some additional benefits to be had by scanning in this way, such
* as the ability to kill a child process, responsible for the actual scanning,
* and yet still have the results of this scanning logged (which, if killed in
* this way prior to completing the scan, would likely involve some type of
* error).
*
* @param string $f The name of the item to be scanned, with path included.
* @param string $ofn The name of the item to be scanned, without any path
* included (so, just the name by itself).
* @return string The scan results, piped back to the parent from the child
* process and returned to the calling function as a string.
*/
$phpMussel['Fork'] = function ($f = '', $ofn = '') use (&$phpMussel) {
$pf = popen(
$phpMussel['Mussel_PHP'] . ' "' . $phpMussel['Vault'] .
'../loader.php" "cli_scan" "' . $f . '" "' . $ofn . '"',
'r'
);
$s = '';
while ($x = fgets($pf)) {
$s .= $x;
}
pclose($pf);
return $s;
};
/** Assigns an array to use for dumping scan debug information (optional). */
$phpMussel['Set-Scan-Debug-Array'] = function (&$Var) use (&$phpMussel) {
if (isset($phpMussel['DebugArr'])) {
unset($phpMussel['DebugArr']);
}
if (!is_array($Var)) {
$Var = [];
}
$phpMussel['DebugArr'] = &$Var;
};
/** Destroys the scan debug array (optional). */
$phpMussel['Destroy-Scan-Debug-Array'] = function (&$Var) use (&$phpMussel) {
unset($phpMussel['DebugArrKey'], $phpMussel['DebugArr']);
$Var = null;
};
/**
* The main scan closure, responsible for initialising scans in most
* circumstances. Should generally be called whenever phpMussel is
* required by external scripts, apps, CMS, etc.
*
* Please refer to Section 3A of the README documentation, "HOW TO USE (FOR WEB
* SERVERS)", for more information.
*
* @param string|array $f Indicates which file, files, directory and/or
* directories to scan (can be a string, an array, or a multidimensional
* array).
* @param bool $n A boolean, indicating the format for the scan results to be
* returned as. False instructs the function to return the results as an
* integer; True instructs the function to return the results as human
* readable text. Optional; Defaults to false.
* @param bool $zz A boolean, indicating to the function whether or not arrayed
* results should be imploded prior to being returned to the calling
* function. False instructs the function to return the arrayed results as
* verbatim; True instructs the function to return the arrayed results as
* an imploded string. Optional; Defaults to false.
* @param int $dpt Represents the current depth of recursion from which the
* function has been called. This information is used for determining how
* far to indent any entries generated for logging and for the display of
* scan results in CLI (you should never manually set this parameter
* yourself).
* @param string $ofn In the context of the initial file upload scanning that
* phpMussel performs when operating via a server, this parameter (a
* string) represents the "original filename" of the file being scanned
* (the original filename, in this context, referring to the name of the
* file being scanned as per supplied by the upload client, as opposed to
* the temporary filename assigned by the server or any other filename).
* When operating in the context of CLI mode, both $f and $ofn represent
* the scan target, as per specified by the CLI operator; The only
* difference between the two is when the scan target is a directory,
* rather than a single file; $f will represent the full path to the file
* (so, directory plus filename), whereas $ofn will represent only the
* filename.
* @return bool|int|string|array The scan results, returned as an array when
* the $f parameter is an array and when $n and/or $zz is/are false, and
* otherwise returned as per described by the README documentation. The
* function may also die the script and return nothing, if something goes
* wrong, such as if the function is triggered in the absense of the
* required $phpMussel['memCache'] variable being set, and may also return
* false, in the absense of the required $phpMussel['HashCache']['Data']
* variable being set.
*/
$phpMussel['Scan'] = function ($f = '', $n = false, $zz = false, $dpt = 0, $ofn = '') use (&$phpMussel) {
if (!isset($phpMussel['memCache'])) {
throw new \Exception(
!isset($phpMussel['lang']['required_variables_not_defined']) ?
'[phpMussel] Required variables aren\'t defined: Can\'t continue.' :
'[phpMussel] ' . $phpMussel['lang']['required_variables_not_defined']
);
}
if (empty($phpMussel['memCache']['OrganisedSigFiles'])) {
$phpMussel['OrganiseSigFiles']();
$phpMussel['memCache']['OrganisedSigFiles'] = true;
}
/** Initialise statistics if they've been enabled. */
$phpMussel['Stats-Initialise']();
if ($phpMussel['EOF']) {
$phpMussel['PrepareHashCache']();
}
if (!isset($phpMussel['HashCache']['Data'])) {
return false;
}
if (!$ofn) {
$ofn = $f;
}
$xst = time() + ($phpMussel['Config']['general']['timeOffset'] * 60);
$xst2822 = $phpMussel['TimeFormat']($xst, $phpMussel['Config']['general']['timeFormat']);
try {
$r = $phpMussel['Recursor']($f, $n, $zz, $dpt, $ofn);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
$xet = time() + ($phpMussel['Config']['general']['timeOffset'] * 60);
$xet2822 = $phpMussel['TimeFormat']($xet, $phpMussel['Config']['general']['timeFormat']);
/** Plugin hook: "after_scan". */
$phpMussel['Execute_Hook']('after_scan');
if ($n && !is_array($r)) {
$r =
$xst2822 . ' ' . $phpMussel['lang']['started'] .
$phpMussel['lang']['_fullstop_final'] . "\n" .
$r . $xet2822 . ' ' . $phpMussel['lang']['finished'] .
$phpMussel['lang']['_fullstop_final'] . "\n";
$phpMussel['WriteScanLog']($r);
}
if (!isset($phpMussel['SkipSerial'])) {
$phpMussel['WriteSerial']($xst, $xet);
}
if ($phpMussel['EOF']) {
if ($phpMussel['Config']['general']['scan_cache_expiry'] > 0) {
foreach ($phpMussel['HashCache']['Data'] as &$phpMussel['ThisItem']) {
if (is_array($phpMussel['ThisItem'])) {
$phpMussel['ThisItem'] = implode(':', $phpMussel['ThisItem']) . ';';
}
}
$phpMussel['SaveCache'](
'HashCache',
$phpMussel['Time'] + $phpMussel['Config']['general']['scan_cache_expiry'],
implode('', $phpMussel['HashCache']['Data'])
);
unset($phpMussel['ThisItem'], $phpMussel['HashCache']['Data']);
}
}
/** Register scan event. */
$phpMussel['Stats-Increment']($phpMussel['EOF'] ? 'API-Events' : 'Web-Events', 1);
/** Update statistics. */
if ($phpMussel['CacheModified']) {
$phpMussel['Statistics'] = $phpMussel['SaveCache']('Statistics', -1, serialize($phpMussel['Statistics']));
}
/** Exit scan process. */
return $r;
};
/**
* Writes to the serialized logfile upon scan completion.
*
* @param string $StartTime When the scan started.
* @param string $FinishTime When the scan finished.
* @return bool True on success; False on failure.
*/
$phpMussel['WriteSerial'] = function ($StartTime = '', $FinishTime = '') use (&$phpMussel) {
$Origin = $phpMussel['Mussel_sapi'] ? 'CLI' : $_SERVER[$phpMussel['Config']['general']['ipaddr']];
$ScanData = empty($phpMussel['whyflagged']) ? $phpMussel['lang']['data_not_available'] : trim($phpMussel['whyflagged']);
if ($phpMussel['Config']['general']['scan_log_serialized']) {
if (!isset($phpMussel['memCache']['objects_scanned'])) {
$phpMussel['memCache']['objects_scanned'] = 0;
}
if (!isset($phpMussel['memCache']['detections_count'])) {
$phpMussel['memCache']['detections_count'] = 0;
}
if (!isset($phpMussel['memCache']['scan_errors'])) {
$phpMussel['memCache']['scan_errors'] = 1;
}
$Handle = [
'Data' => serialize([
'start_time' => $StartTime ?: (isset($phpMussel['memCache']['start_time']) ? $phpMussel['memCache']['start_time'] : '-'),
'end_time' => $FinishTime ?: (isset($phpMussel['memCache']['end_time']) ? $phpMussel['memCache']['end_time'] : '-'),
'origin' => $Origin,
'objects_scanned' => $phpMussel['memCache']['objects_scanned'],
'detections_count' => $phpMussel['memCache']['detections_count'],
'scan_errors' => $phpMussel['memCache']['scan_errors'],
'detections' => $ScanData
]) . "\n",
'File' => $phpMussel['TimeFormat']($phpMussel['Time'], $phpMussel['Config']['general']['scan_log_serialized'])
];
$WriteMode = (!file_exists($phpMussel['Vault'] . $Handle['File']) || (
$phpMussel['Config']['general']['truncate'] > 0 &&
filesize($phpMussel['Vault'] . $Handle['File']) >= $phpMussel['ReadBytes']($phpMussel['Config']['general']['truncate'])
)) ? 'w' : 'a';
$Stream = fopen($phpMussel['Vault'] . $Handle['File'], $WriteMode);
fwrite($Stream, $Handle['Data']);
fclose($Stream);
return true;
}
return false;
};
/**
* Writes to the standard scan log upon scan completion.
*
* @param string $Data What to write.
* @return bool True on success; False on failure.
*/
$phpMussel['WriteScanLog'] = function ($Data) use (&$phpMussel) {
if ($phpMussel['Config']['general']['scan_log']) {
$File = $phpMussel['TimeFormat']($phpMussel['Time'], $phpMussel['Config']['general']['scan_log']);
if (!file_exists($phpMussel['Vault'] . $File)) {
$Data = $phpMussel['safety'] . "\n" . $Data;
}
$WriteMode = (!file_exists($phpMussel['Vault'] . $File) || (
$phpMussel['Config']['general']['truncate'] > 0 &&
filesize($phpMussel['Vault'] . $File) >= $phpMussel['ReadBytes']($phpMussel['Config']['general']['truncate'])
)) ? 'w' : 'a';
$Handle = fopen($phpMussel['Vault'] . $File, 'a');
fwrite($Handle, $Data);
fclose($Handle);
return true;
}
return false;
};
/**
* A simple closure for replacing date/time placeholders with corresponding
* date/time information. Used by the logfiles and some timestamps.
*
* @param int $Time A unix timestamp.
* @param string|array $In An input or an array of inputs to manipulate.
* @return string|array The adjusted input(/s).
*/
$phpMussel['TimeFormat'] = function ($Time, $In) use (&$phpMussel) {
$Time = date('dmYHisDMP', $Time);
$values = [
'dd' => substr($Time, 0, 2),
'mm' => substr($Time, 2, 2),
'yyyy' => substr($Time, 4, 4),
'yy' => substr($Time, 6, 2),
'hh' => substr($Time, 8, 2),
'ii' => substr($Time, 10, 2),
'ss' => substr($Time, 12, 2),
'Day' => substr($Time, 14, 3),
'Mon' => substr($Time, 17, 3),
'tz' => substr($Time, 20, 3) . substr($Time, 24, 2),
't:z' => substr($Time, 20, 6)
];
$values['d'] = (int)$values['dd'];
$values['m'] = (int)$values['mm'];
return is_array($In) ? array_map(function ($Item) use (&$values, &$phpMussel) {
return $phpMussel['ParseVars']($values, $Item);
}, $In) : $phpMussel['ParseVars']($values, $In);
};
/**
* Normalises values defined by the YAML closure.
*
* @param string|int|bool $Value The value to be normalised.
* @param int $ValueLen The length of the value to be normalised.
* @param string|int|bool $ValueLow The value to be normalised, lowercased.
*/
$phpMussel['YAML-Normalise-Value'] = function (&$Value, $ValueLen, $ValueLow) {
if (substr($Value, 0, 1) === '"' && substr($Value, $ValueLen - 1) === '"') {
$Value = substr($Value, 1, $ValueLen - 2);
} elseif (substr($Value, 0, 1) === '\'' && substr($Value, $ValueLen - 1) === '\'') {
$Value = substr($Value, 1, $ValueLen - 2);
} elseif ($ValueLow === 'true' || $ValueLow === 'y') {
$Value = true;
} elseif ($ValueLow === 'false' || $ValueLow === 'n') {
$Value = false;
} elseif (substr($Value, 0, 2) === '0x' && ($HexTest = substr($Value, 2)) && !preg_match('/[^a-f0-9]/i', $HexTest) && !($ValueLen % 2)) {
$Value = hex2bin($HexTest);
} else {
$ValueInt = (int)$Value;
if (strlen($ValueInt) === $ValueLen && $Value == $ValueInt && $ValueLen > 1) {
$Value = $ValueInt;
}
}
if (!$Value) {
$Value = false;
}
};
/**
* A simplified YAML-like parser. Note: This is intended to adequately serve
* the needs of this package in a way that should feel familiar to users of
* YAML, but it isn't a true YAML implementation and it doesn't adhere to any
* specifications, official or otherwise.
*
* @param string $In The data to parse.
* @param array $Arr Where to save the results.
* @param bool $VM Validator Mode (if true, results won't be saved).
* @param int $Depth Tab depth (inherited through recursion; ignore it).
* @return bool Returns false if errors are encountered, and true otherwise.
*/
$phpMussel['YAML'] = function ($In, &$Arr, $VM = false, $Depth = 0) use (&$phpMussel) {
if (!is_array($Arr)) {
if ($VM) {
return false;
}
$Arr = [];
}
if (!substr_count($In, "\n")) {
return false;
}
$In = str_replace("\r", '', $In);
$Key = $Value = $SendTo = '';
$TabLen = $SoL = 0;
while ($SoL !== false) {
if (($EoL = strpos($In, "\n", $SoL)) === false) {
$ThisLine = substr($In, $SoL);
} else {
$ThisLine = substr($In, $SoL, $EoL - $SoL);
}
$SoL = ($EoL === false) ? false : $EoL + 1;
$ThisLine = preg_replace(["/#.*$/", "/\x20+$/"], '', $ThisLine);
if (empty($ThisLine) || $ThisLine === "\n") {
continue;
}
$ThisTab = 0;
while (($Chr = substr($ThisLine, $ThisTab, 1)) && ($Chr === ' ' || $Chr === "\t")) {
$ThisTab++;
}
if ($ThisTab > $Depth) {
if ($TabLen === 0) {
$TabLen = $ThisTab;
}
$SendTo .= $ThisLine . "\n";
continue;
} elseif ($ThisTab < $Depth) {
return false;
} elseif (!empty($SendTo)) {
if (empty($Key)) {
return false;
}
if (!isset($Arr[$Key])) {
if ($VM) {
return false;
}
$Arr[$Key] = false;
}
if (!$phpMussel['YAML']($SendTo, $Arr[$Key], $VM, $TabLen)) {
return false;
}
$SendTo = '';
}
if (substr($ThisLine, -1) === ':') {
$Key = substr($ThisLine, $ThisTab, -1);
$KeyLen = strlen($Key);
$KeyLow = strtolower($Key);
$phpMussel['YAML-Normalise-Value']($Key, $KeyLen, $KeyLow);
if (!isset($Arr[$Key])) {
if ($VM) {
return false;
}
$Arr[$Key] = false;
}
} elseif (substr($ThisLine, $ThisTab, 2) === '- ') {
$Value = substr($ThisLine, $ThisTab + 2);
$ValueLen = strlen($Value);
$ValueLow = strtolower($Value);
$phpMussel['YAML-Normalise-Value']($Value, $ValueLen, $ValueLow);
if (!$VM && $ValueLen > 0) {
$Arr[] = $Value;
}
} elseif (($DelPos = strpos($ThisLine, ': ')) !== false) {
$Key = substr($ThisLine, $ThisTab, $DelPos - $ThisTab);
$KeyLen = strlen($Key);
$KeyLow = strtolower($Key);
$phpMussel['YAML-Normalise-Value']($Key, $KeyLen, $KeyLow);
if (!$Key) {
return false;
}
$Value = substr($ThisLine, $ThisTab + $KeyLen + 2);
$ValueLen = strlen($Value);
$ValueLow = strtolower($Value);
$phpMussel['YAML-Normalise-Value']($Value, $ValueLen, $ValueLow);
if (!$VM && $ValueLen > 0) {
$Arr[$Key] = $Value;
}
} elseif (strpos($ThisLine, ':') === false && strlen($ThisLine) > 1) {
$Key = $ThisLine;
$KeyLen = strlen($Key);
$KeyLow = strtolower($Key);
$phpMussel['YAML-Normalise-Value']($Key, $KeyLen, $KeyLow);
if (!isset($Arr[$Key])) {
if ($VM) {
return false;
}
$Arr[$Key] = false;
}
}
}
if (!empty($SendTo) && !empty($Key)) {
if (!isset($Arr[$Key])) {
if ($VM) {
return false;
}
$Arr[$Key] = [];
}
if (!$phpMussel['YAML']($SendTo, $Arr[$Key], $VM, $TabLen)) {
return false;
}
}
return true;
};
/**
* Fix incorrect typecasting for some for some variables that sometimes default
* to strings instead of booleans or integers.
*/
$phpMussel['AutoType'] = function (&$Var, $Type = '') use (&$phpMussel) {
if ($Type === 'string' || $Type === 'timezone') {
$Var = (string)$Var;
} elseif ($Type === 'int' || $Type === 'integer') {
$Var = (int)$Var;
} elseif ($Type === 'real' || $Type === 'double' || $Type === 'float') {
$Var = (real)$Var;
} elseif ($Type === 'bool' || $Type === 'boolean') {
$Var = (strtolower($Var) !== 'false' && $Var);
} elseif ($Type === 'kb') {
$Var = $phpMussel['ReadBytes']($Var, 1);
} else {
$LVar = strtolower($Var);
if ($LVar === 'true') {
$Var = true;
} elseif ($LVar === 'false') {
$Var = false;
} elseif ($Var !== true && $Var !== false) {
$Var = (int)$Var;
}
}
};
/**
* Used to send cURL requests.
*
* @param string $URI The resource to request.
* @param array $Params (Optional) An associative array of key-value pairs to
* to send along with the request.
* @return string The results of the request.
*/
$phpMussel['Request'] = function ($URI, $Params = '', $Timeout = '') use (&$phpMussel) {
if (!$Timeout) {
$Timeout = $phpMussel['Timeout'];
}
/** Initialise the cURL session. */
$Request = curl_init($URI);
$LCURI = strtolower($URI);
$SSL = (substr($LCURI, 0, 6) === 'https:');
curl_setopt($Request, CURLOPT_FRESH_CONNECT, true);
curl_setopt($Request, CURLOPT_HEADER, false);
if (empty($Params)) {
curl_setopt($Request, CURLOPT_POST, false);
} else {
curl_setopt($Request, CURLOPT_POST, true);
curl_setopt($Request, CURLOPT_POSTFIELDS, $Params);
}
if ($SSL) {
curl_setopt($Request, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
curl_setopt($Request, CURLOPT_SSL_VERIFYPEER, false);
}
curl_setopt($Request, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($Request, CURLOPT_MAXREDIRS, 1);
curl_setopt($Request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($Request, CURLOPT_TIMEOUT, $Timeout);
curl_setopt($Request, CURLOPT_USERAGENT, $phpMussel['ScriptUA']);
/** Execute and get the response. */
$Response = curl_exec($Request);
/** Close the cURL session. */
curl_close($Request);
/** Return the results of the request. */
return $Response;
};
/**
* Used to generate new salts when necessary, which may be occasionally used by
* some specific optional peripheral features (note: should not be considered
* cryptographically secure; especially so for versions of PHP < 7).
*
* @return string Salt.
*/
$phpMussel['GenerateSalt'] = function () {
static $MinLen = 32;
static $MaxLen = 72;
static $MinChr = 1;
static $MaxChr = 255;
$Salt = '';
if (function_exists('random_int')) {
try {
$Length = random_int($MinLen, $MaxLen);
} catch (\Exception $e) {
$Length = rand($MinLen, $MaxLen);
}
} else {
$Length = rand($MinLen, $MaxLen);
}
if (function_exists('random_bytes')) {
try {
$Salt = random_bytes($Length);
} catch (\Exception $e) {
$Salt = '';
}
}
if (empty($Salt)) {
if (function_exists('random_int')) {
try {
for ($Index = 0; $Index < $Length; $Index++) {
$Salt .= chr(random_int($MinChr, $MaxChr));
}
} catch (\Exception $e) {
$Salt = '';
for ($Index = 0; $Index < $Length; $Index++) {
$Salt .= chr(rand($MinChr, $MaxChr));
}
}
} else {
for ($Index = 0; $Index < $Length; $Index++) {
$Salt .= chr(rand($MinChr, $MaxChr));
}
}
}
return $Salt;
};
/** Clears expired entries from a list. */
$phpMussel['ClearExpired'] = function (&$List, &$Check) use (&$phpMussel) {
if ($List) {
$End = 0;
while (true) {
$Begin = $End;
if (!$End = strpos($List, "\n", $Begin + 1)) {
break;
}
$Line = substr($List, $Begin, $End - $Begin);
if ($Split = strrpos($Line, ',')) {
$Expiry = (int)substr($Line, $Split + 1);
if ($Expiry < $phpMussel['Time']) {
$List = str_replace($Line, '', $List);
$End = 0;
$Check = true;
}
}
}
}
};
/** Fetch information about signature files and prepare for use with the scan process. */
$phpMussel['OrganiseSigFiles'] = function () use (&$phpMussel) {
if (empty($phpMussel['Config']['signatures']['Active'])) {
return false;
}
$Classes = [
'General_Command_Detections',
'Filename',
'Hash',
'Standard',
'Standard_RegEx',
'Normalised',
'Normalised_RegEx',
'HTML',
'HTML_RegEx',
'PE_Extended',
'PE_Sectional',
'Complex_Extended',
'URL_Scanner'
];
$List = explode(',', $phpMussel['Config']['signatures']['Active']);
foreach ($List as $File) {
$Handle = fopen($phpMussel['sigPath'] . $File, 'rb');
if (fread($Handle, 9) !== 'phpMussel') {
fclose($Handle);
continue;
}
$Class = fread($Handle, 1);
fclose($Handle);
$Nibbles = $phpMussel['split_nibble']($Class);
if (!empty($Classes[$Nibbles[0]])) {
if (!isset($phpMussel['memCache'][$Classes[$Nibbles[0]]])) {
$phpMussel['memCache'][$Classes[$Nibbles[0]]] = ',';
}
$phpMussel['memCache'][$Classes[$Nibbles[0]]] .= $File . ',';
}
}
};
/** A simple safety wrapper for unpack. */
$phpMussel['UnpackSafe'] = function ($Format, $Data) {
return (strlen($Data) > 1) ? unpack($Format, $Data) : '';
};
/** A simple safety wrapper for hex2bin. */
$phpMussel['HexSafe'] = function ($Data) use (&$phpMussel) {
return ($Data && !preg_match('/[^a-f0-9]/i', $Data) && !(strlen($Data) % 2)) ? $phpMussel['Function']('HEX', $Data) : '';
};
/** If input isn't an array, make it so. Remove empty elements. */
$phpMussel['Arrayify'] = function (&$Input) {
if (!is_array($Input)) {
$Input = [$Input];
}
$Input = array_filter($Input);
};
/**
* Read byte value configuration directives as byte values.
*
* @param string $In Input.
* @param int $Mode Operating mode. 0 for true byte values, 1 for validating.
* Default is 0.
* @return string|int Output (depends on operating mode).
*/
$phpMussel['ReadBytes'] = function ($In, $Mode = 0) {
if (preg_match('/[KMGT][oB]$/i', $In)) {
$Unit = substr($In, -2, 1);
} elseif (preg_match('/[KMGToB]$/i', $In)) {
$Unit = substr($In, -1);
}
$Unit = isset($Unit) ? strtoupper($Unit) : 'K';
$In = (real)$In;
if ($Mode === 1) {
return $Unit === 'B' || $Unit === 'o' ? $In . 'B' : $In . $Unit . 'B';
}
$Multiply = ['K' => 1024, 'M' => 1048576, 'G' => 1073741824, 'T' => 1099511627776];
return (int)floor($In * (isset($Multiply[$Unit]) ? $Multiply[$Unit] : 1));
};
/**
* Improved recursive directory iterator for phpMussel.
*
* @param string $Base Directory root.
* @return array Directory tree.
*/
$phpMussel['DirectoryRecursiveList'] = function ($Base) {
$Arr = [];
$Key = -1;
$Offset = strlen($Base);
$List = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($Base), RecursiveIteratorIterator::SELF_FIRST);
foreach ($List as $Item => $List) {
if (preg_match('~^(?:/\.\.|./\.|\.{3})$~', str_replace("\\", '/', substr($Item, -3))) || !is_readable($Item) || is_dir($Item)) {
continue;
}
$Key++;
$Arr[$Key] = substr($Item, $Offset);
}
return $Arr;
};
/**
* Internal aliases for common hash functions (used by CLI).
*
* @param string $Alias The alias used.
* @param string $Data The data to be hashed.
* @return string The output hash.
*/
$phpMussel['HashAlias'] = function ($Alias, $Data) {
if ($Alias === 'm' || $Alias === 'md5' || $Alias === 'md5_file') {
return md5($Data);
}
if ($Alias === 'sha1' || $Alias === 'sha1_file') {
return sha1($Data);
}
return '';
};
/**
* Duplication avoidance (forking the process via recursive CLI mode commands).
*
* @param string $Command Command with parameters for the action to be taken.
* @param callable $Callable Executed normally when not forking the process.
* @return string Returnable data to be echoed to the CLI output.
*/
$phpMussel['CLI-RecursiveCommand'] = function ($Command, $Callable) use (&$phpMussel) {
$Params = substr($Command, strlen($phpMussel['cmd']) + 1);
if (is_dir($Params)) {
if (!is_readable($Params)) {
return $phpMussel['lang']['failed_to_access'] . '"' . $Params . "\".\n";
}
$Decal = [':-) - (-:', ':-) \\ (-:', ':-) | (-:', ':-) / (-:'];
$Frame = 0;
$Terminal = $Params[strlen($Params) - 1];
if ($Terminal !== "\\" && $Terminal !== '/') {
$Params .= '/';
}
$List = $phpMussel['DirectoryRecursiveList']($Params);
$Returnable = '';
foreach ($List as $Item) {
echo "\r" . $Decal[$Frame];
$Returnable .= $phpMussel['Fork']($phpMussel['cmd'] . ' ' . $Params . $Item, $Item) . "\n";
$Frame = $Frame < 3 ? $Frame + 1 : 0;
}
echo "\r ";
return $Returnable;
}
return is_file($Params) ? $Callable($Params) : $Params . $phpMussel['lang']['cli_is_not_a'] . "\n";
};
/** Handles errors (will expand this later). */
$phpMussel['ErrorHandler_1'] = function ($errno) use (&$phpMussel) {
return;
};
/** Duplication avoidance (some file handling for honeypot functionality). */
$phpMussel['ReadFile-For-Honeypot'] = function (&$Array, $File) use (&$phpMussel) {
if (!isset($Array['qdata'])) {
$Array['qdata'] = '';
}
$Array['odata'] = $phpMussel['ReadFile']($File);
$Array['len'] = strlen($Array['odata']);
$Array['crc'] = hash('crc32b', $Array['odata']);
$Array['qfile'] = $phpMussel['Time'] . '-' . md5($phpMussel['Config']['general']['quarantine_key'] . $Array['crc'] . $phpMussel['Time']);
if ($Array['len'] > 0 && $Array['len'] < $phpMussel['ReadBytes']($phpMussel['Config']['general']['quarantine_max_filesize'])) {
$phpMussel['Quarantine'](
$Array['odata'],
$phpMussel['Config']['general']['quarantine_key'],
$_SERVER[$phpMussel['Config']['general']['ipaddr']],
$Array['qfile']
);
}
if ($phpMussel['Config']['general']['delete_on_sight'] && is_readable($File)) {
unlink($File);
}
$Array['qdata'] .= sprintf(
'TEMP FILENAME: %1$s%6$sFILENAME: %2$s%6$sFILESIZE: %3$s%6$sMD5: %4$s%6$s%5$s',
$File,
urlencode($File),
$Array['len'],
md5($Array['odata']),
$phpMussel['ParseVars'](['QFU' => $Array['qfile']], $phpMussel['lang']['quarantined_as']),
"\n"
);
};
/** Duplication avoidance (assigning kill details and unlinking files). */
$phpMussel['KillAndUnlink'] = function () use (&$phpMussel) {
$phpMussel['killdata'] .=
'-UPLOAD-LIMIT-EXCEEDED--NO-HASH-:' .
$phpMussel['upload']['FilesData']['FileSet']['size'][$phpMussel['upload']['FilesData']['FileSet']['i']] . ':' .
$phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['upload']['FilesData']['FileSet']['i']] . "\n";
$phpMussel['whyflagged'] .=
$phpMussel['lang']['upload_limit_exceeded'] .
' (' . $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['upload']['FilesData']['FileSet']['i']] . ')' .
$phpMussel['lang']['_exclamation'];
if (
$phpMussel['Config']['general']['delete_on_sight'] &&
is_uploaded_file($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['upload']['FilesData']['FileSet']['i']]) &&
is_readable($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['upload']['FilesData']['FileSet']['i']]) &&
!$phpMussel['Config']['general']['honeypot_mode']
) {
unlink($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['upload']['FilesData']['FileSet']['i']]);
}
};
/** Increments statistics if they've been enabled. */
$phpMussel['Stats-Increment'] = function ($Statistic, $Amount) use (&$phpMussel) {
if ($phpMussel['Config']['general']['statistics'] && isset($phpMussel['Statistics'][$Statistic])) {
$phpMussel['Statistics'][$Statistic] += $Amount;
$phpMussel['CacheModified'] = true;
}
};
/** Initialise statistics if they've been enabled. */
$phpMussel['Stats-Initialise'] = function () use (&$phpMussel) {
if ($phpMussel['Config']['general']['statistics']) {
$phpMussel['CacheModified'] = false;
if ($phpMussel['Statistics'] = ($phpMussel['FetchCache']('Statistics') ?: [])) {
$phpMussel['Statistics'] = unserialize($phpMussel['Statistics']) ?: [];
}
if (empty($phpMussel['Statistics']['Other-Since'])) {
$phpMussel['Statistics'] = [
'Other-Since' => $phpMussel['Time'],
'Web-Events' => 0,
'Web-Scanned' => 0,
'Web-Blocked' => 0,
'Web-Quarantined' => 0,
'CLI-Events' => 0,
'CLI-Scanned' => 0,
'CLI-Flagged' => 0,
'API-Events' => 0,
'API-Scanned' => 0,
'API-Flagged' => 0
];
$phpMussel['CacheModified'] = true;
}
}
};