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('&#', '&#', str_ireplace('&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([ '@]*?>.*?@si', '@<[\/\!]*?[^<>]*?>@si', '@]*?>.*?@siU', '@@' ], '', $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; } } };