<?php define('DS', DIRECTORY_SEPARATOR); define('FILE', str_replace('/', DIRECTORY_SEPARATOR, basename((string) $_SERVER['SCRIPT_FILENAME']))); // No max execution time @ini_set('max_execution_time', '0'); // No limit @ini_set('memory_limit', '-1'); // Remove time limit; avoid 504 HTTP errors @set_time_limit(0); /** * Remove recursively folders * (f.i. rrmdir(__DIR__/hashes/joomla/2.5.27) will kill the full tree below the specified folder). * * @param type $folder * @param type $killroot If true, the folder himself will be removed. * rrmdir(__DIR__/hashes/joomla/2.5.27, true) ==> remove folder 2.5.27 too and not only his children * @param type $arrIgnoreFiles * * @return boolean */ function rrmdir($folder, $killroot = false, $arrIgnoreFiles = ['.htaccess', 'index.html']) { $return = true; if (is_dir($folder)) { $dir_handle = opendir($folder); } if (!$dir_handle) { return false; } while ($file = readdir($dir_handle)) { if ('.' != $file && '..' != $file) { if (!is_dir($folder . '/' . $file)) { if (!in_array($file, $arrIgnoreFiles)) { @unlink($folder . '/' . $file); } } else { rrmdir($folder . '/' . $file, true, $arrIgnoreFiles); } } } closedir($dir_handle); if (true === $killroot) { @rmdir($folder); } return $return; } function json_val($string) { // decode the JSON data $result = json_decode($string); // switch and check possible JSON errors switch (json_last_error()) { case JSON_ERROR_NONE: // JSON is valid // No error has occurred $error = ''; break; case JSON_ERROR_DEPTH: $error = 'The maximum stack depth has been exceeded.'; break; case JSON_ERROR_STATE_MISMATCH: $error = 'Invalid or malformed JSON.'; break; case JSON_ERROR_CTRL_CHAR: $error = 'Control character error, possibly incorrectly encoded.'; break; case JSON_ERROR_SYNTAX: $error = 'Syntax error, malformed JSON.'; break; case JSON_ERROR_UTF8: $error = 'Malformed UTF-8 characters, possibly incorrectly encoded.'; break; case JSON_ERROR_RECURSION: $error = 'One or more recursive references in the value to be encoded.'; break; case JSON_ERROR_INF_OR_NAN: $error = 'One or more NAN or INF values in the value to be encoded.'; break; case JSON_ERROR_UNSUPPORTED_TYPE: $error = 'A value of a type that cannot be encoded was given.'; break; default: $error = 'Unknown JSON error occurred.'; break; } if ('' !== $error) { // throw the Exception or exit // or whatever :) exit($error); } // everything is OK return $result; } function recurceZip($path) { $all_path = []; $files = null; if (file_exists($path) && is_dir($path)) { $files = array_diff(scandir($path), array('..', '.')); } if ($files) { foreach ($files as $key => $value) { $file_info = pathinfo($value); if (isset($file_info['extension']) && $file_info['extension'] === 'zip') { $file_name = $path . '/' . $file_info['basename']; $zip = new ZipArchive(); if ($zip->open($file_name) === true) { if (is_dir($path . '/' . $file_info['filename'])) { // zip déjà traité echo '<p class="text-danger">'.$file_name.' already unzipped.</p>'; $zip->close(); unlink($file_name); continue;// ignore } $all_path[] = $path . '/' . $file_info['filename']; $zip->extractTo($path . '/' . $file_info['filename']); $zip->close(); unlink($file_name); } else { echo '<p class="text-danger">Error unzipping '.$file_name.'.</p>'; } } if (!isset($file_info['extension'])) { $all_path[] = $path . '/' . $file_info['filename']; } } } foreach ($all_path as $k => $v) { $path = $v; recurceZip($path); } return; } function makeJSON($folder, $filename) { // Make a JSON file to retrieve all MD5 hashes $arrFiles = []; $dir = new RecursiveDirectoryIterator($folder . DS, RecursiveDirectoryIterator::SKIP_DOTS); $files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::LEAVES_ONLY); foreach ($files as $name => $object) { $arrFiles[] = $name; } if (count($arrFiles) > 0) { if (file_exists($filename)) { $json = json_decode(file_get_contents($filename), true); if (0 == count($json)) { echo '<h1>Problem reading file ' . $filename . '. Error reading json</h1>'; die(); } // Keep the filesize before any changes clearstatcache(); $fsize = filesize($filename); } else { $json = []; $fsize = 0; } foreach ($arrFiles as $file) { // Skippe readme.txt file in the root folder if ($file == $folder . DS . 'readme.txt') { continue; } // ignore installation directory if (str_starts_with($file, $folder . DS .'installation')) { continue; } $relativefName = str_replace($folder, '', $file); // If the filename is called aesecure_quickscan.whitelist.json or // just whitelist.json, open it and process every entries of it if (in_array($file, [$folder . DS . 'aesecure_quickscan.whitelist.json', $folder . DS . 'whitelist.json'])) { $white = json_decode(file_get_contents($file), true); foreach ($white as $md5 => $fname) { if (!(isset($json[$md5]))) { $json[$md5] = $relativefName; } } } else { // It's a normal file, calculate the md5 of the file itself $md5 = @md5_file($file); if (false !== $md5) { if (!(isset($json[$md5]))) { $json[$md5] = '1'; } // Get the hash for both UNIX and WINDOWS system $md5 = md5(str_replace("\r\n", "\n", file_get_contents($file))); if (!(isset($json[$md5]))) { $json[$md5] = '1'; } $md5 = md5(str_replace("\n", "\r\n", file_get_contents($file))); if (!(isset($json[$md5]))) { $json[$md5] = '1'; } } } // and kill the file, no more needed if (in_array(basename($filename), ['blacklist.json', 'other.json'])) { @unlink($file); } } // Security, check that we've at least one file if (count($json) > 0) { if (json_val(json_encode($json))) { asort($json); if (file_exists($filename)) { copy($filename, $filename . '.backup'); } // Output the file with all hashes $fp = fopen($filename, 'w'); fwrite($fp, json_encode($json)); fclose($fp); unset($fp); clearstatcache(); if (filesize($filename) < $fsize) { unlink($filename); rename($filename . '.backup', $filename); echo '<h1 class=‘text-danger’>There has been a problem with updating the ' . $filename . '. The file prior to the changes has been restored.</h1>'; } else { // Ok, backup file isn't needed, everything was ok. if (file_exists($filename . '.backup')) { unlink($filename . '.backup'); } } } } } if (in_array(basename($filename), ['blacklist.json', 'other.json'])) { rrmdir($folder = $folder, $killroot = false, $arrIgnoreFiles = ['readme.txt']); } } function toDo() { $arrFolder = [ ['joomla' => 'J!'], ['wordpress' => 'WP'], ['J!extensions' => ''], ['WPextensions' => ''] ]; $out = ''; foreach ($arrFolder as $arr) { $Folder = key($arr); // For instance "joomla"; name of the CMS; name of the subfolder $prefix = $arr[$Folder]; // For instance "J!"; prefix to use for naming files (f.i. "J!3.4.1" for the Joomla file for version 3.4.1) $hashFolder = __DIR__ . DS . 'hashes'; if (!is_dir($hashFolder)) { @mkdir($hashFolder); } // Check if we have a folder called __DIR__/hashes/joomla (or wordpress or ...) $hashFolder = __DIR__ . DS . 'hashes' . DS . $Folder; if (!is_dir($hashFolder)) { @mkdir($hashFolder); } $files = array_diff(scandir($hashFolder), array('..', '.')); foreach ($files as $key => $value) { $file_info = pathinfo($value); if (isset($file_info['extension']) && $file_info['extension'] === 'zip') { $out .= '<p class="text-info">'.$hashFolder.DS.$file_info['filename'].' to be processed.</p>'; } } } return $out; } function process() { if (!isset($_GET['task'])) { return ""; } $arrFolder = [ ['joomla' => 'J!'], ['wordpress' => 'WP'], ['J!extensions' => ''], ['WPextensions' => ''] ]; $out = ''; foreach ($arrFolder as $arr) { $Folder = key($arr); // For instance "joomla"; name of the CMS; name of the subfolder $prefix = $arr[$Folder]; // For instance "J!"; prefix to use for naming files (f.i. "J!3.4.1" for the Joomla file for version 3.4.1) // Check if we have a folder called __DIR__/hashes/joomla (or wordpress or ...) $hashFolder = __DIR__ . DS . 'hashes' . DS . $Folder; if (!is_dir($hashFolder)) { @mkdir($hashFolder); } if (is_dir($hashFolder)) { recurceZip($hashFolder); // unzip all zip files if (!in_array($Folder, ['blacklist', 'other'])) { // This is a folder like "Joomla" : one json file by subfolder since a subfolder contain a specific version of that CMS $subfolders = array_filter(glob($hashFolder . DS . '*'), 'is_dir'); } else { // Only one single file for everything present in the "other" folder $subfolders = [$Folder]; } if (count($subfolders) > 0) { $tmp = ''; foreach ($subfolders as $folder) { // The file with the hashes will be something like hashes/joomla/J!2.5.27.json if (!in_array($Folder, ['blacklist', 'other'])) { $filename = $hashFolder . DS . $prefix . str_replace($hashFolder . DS, '', $folder) . '.json'; } else { $filename = dirname(dirname($hashFolder)) . DS . $Folder . '.json'; $folder = $hashFolder; } if ((!file_exists($filename)) || (in_array($Folder, ['blacklist', 'other']))) { makeJSON($folder, $filename); $out .= '<p class="text-success">'.$filename.' has been created.</p>'; } else { $out .= '<p class="text-danger">'.$filename.' has been ignored.</p>'; } if (is_dir($folder)) { // unzipped folder ? rrmdir($folder, true, []); } } if ('' != $tmp) { $out .= '<h3>Scan ' . $hashFolder . '</h3>'; $out .= '<ol>' . $tmp . '</ol>'; } } } else { $out .= '<p>Folder ' . $hashFolder . ' not found</p>'; } } if ($out == "") { $out = '<p>Nothing to do.....</p>'; } return $out.'<hr/>'; } $showInfo = process(); if ($showInfo) { $res = json_encode(['result' => $showInfo]); exit($res); } $todo = toDo(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr"> <head> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet" /> <title>aeSecure - Make hash</title> </head> <body class="container"> <h1>aeSecure QuickScan - Make hash</h1> <p>To add a new version of a CMS or an extension, you need to</p> <ol> <li>create the folder corresponding to this CMS (hashes/joomla, hashes/wordpress, hashes/J!extensions, hashes/WPextensions)</li> <li>copy zip files in the directory. <li>note : in case of Joomla or WP, rename zip file so it just contains its version number (e.g. hashes/joomla/5.2.0.zip)</li> </ol> <p>The JSON files will be generated automatically.</p> <hr/> <div id="todo"><?php echo $todo;?></div> <button type="button" id="btnSubmit" class="btn btn-warning" data-toggle="popover" data-placement="bottom" data-html="true" data-content="<strong class='text-danger'>GO</strong>"> <span class="glyphicon glyphicon-trash"> </span> GO </button> <div id="result"><?php echo $showInfo;?></div> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js"></script> <script defer="defer"> document.addEventListener('DOMContentLoaded', function() { button = document.getElementById('btnSubmit'); button.addEventListener('click',function (e) { document.getElementById("btnSubmit").disabled = true; e.stopImmediatePropagation(); var xhr = new XMLHttpRequest(); xhr.responseType = 'text'; url = "?task=submit"; xhr.open('GET', url); xhr.onload = function() { if (xhr.readyState === xhr.DONE) { if (this.status === 200) { var info = JSON.parse(this.response); if (info.result) { result = document.getElementById('result'); result.innerHTML = result.innerHTML + info.result; todo = document.getElementById('todo'); todo.innerHTML = ""; document.getElementById("btnSubmit").style.display = "none"; } } else { ocument.getElementById("btnSubmit").disabled = false; } } }; xhr.send(); }) }); </script> </html>