/** *UNCATCHABLE ERRORS */ public static function captureShutdown() { if (defined('_BF_LAST_BREATH')) { bfLog::log('Tock with dying breath said: ' . _BF_LAST_BREATH); } else { bfLog::log('Tock'); } }
/** * deletes a directory recursively * Bf_Filesystem::deleteRecursive(JPATH_ROOT .'/tmp', TRUE); * * @param string $target * @param bool $ignoreWarnings * @param array $msg * * @return bool */ static function deleteRecursive($target, $ignoreWarnings = FALSE, $msg = array()) { $exceptions = array('.', '..'); if (!($sourceDir = @opendir($target))) { if ($ignoreWarnings !== TRUE) { $msg['result'] = 'failure'; $msg['errors'][] = $target; return $msg; } } if (!$sourceDir) { return; } while (FALSE !== ($sibling = readdir($sourceDir))) { if (!in_array($sibling, $exceptions)) { $object = str_replace('//', '/', $target . '/' . $sibling); if (is_dir($object)) { $msg = Bf_Filesystem::deleteRecursive($object, $ignoreWarnings, $msg); } if (is_file($object)) { bfLog::log('Deleting ' . $object); $result = unlink($object); if ($result) { $msg['deleted_files'][] = $object; } if (!$result) { $msg['errors'][] = $object; } } } } closedir($sourceDir); if (is_dir($target)) { bfLog::log('Deleting ' . $target); if ($result = rmdir($target)) { $msg['deleted_folders'][] = $target; $msg['result'] = 'success'; } else { bfLog::log(sprintf('Deleting %S FAILED', $target)); $msg['result'] = 'failure'; } } else { } return $msg; }
/** * Get a JSON formatted list of installed extensions * Needs to be public so we can call it from the audit. * * @return string */ public function getExtensions() { // connect/get the Joomla db object $this->db = JFactory::getDbo(); // crazy way of handling Joomla 1.5.x legacy :-( $one5 = FALSE; // Get Joomla 2.0+ Extensions $this->db->setQuery('SELECT e.extension_id, e.name, e.type, e.element, e.enabled, e.folder, ( SELECT title FROM #__menu AS m WHERE m.component_id = e.extension_id AND parent_id = 1 ORDER BY ID ASC LIMIT 1 ) AS title FROM #__extensions AS e WHERE protected = 0'); $installedExtensions = $this->db->loadObjectList(); // ok if we have none maybe we are Joomla < 1.5.26 if (!$installedExtensions) { // Yooo hoo I'm on a crap old, out of date, probably hackable Joomla version! $one5 = TRUE; // Get the extensions - used to be called components $this->db->setQuery('SELECT "component" as "type", name, `option` as "element", enabled FROM #__components WHERE iscore != 1 and parent = 0'); $components = $this->db->loadObjectList(); // Get the plugins $this->db->setQuery('SELECT "plugin" as "type", name, element, folder, published as enabled FROM #__plugins WHERE iscore != 1'); $plugins = $this->db->loadObjectList(); // get the modules $this->db->setQuery('SELECT "module" as "type", module, module as name, client_id, published as enabled FROM #__modules WHERE iscore != 1'); $modules = $this->db->loadObjectList(); /** * Get the templates - I n Joomla 1.5.x the templates are not in the * db unless published so we need to read the folders from the /templates folders * Note in Joomla 1.5.x there was no such think as admin templates */ $folders = array_merge(scandir(JPATH_BASE . '/templates'), scandir(JPATH_ADMINISTRATOR . '/templates')); $templates = array(); foreach ($folders as $templateFolder) { $f = JPATH_BASE . '/templates/' . trim($templateFolder); $a = JPATH_ADMINISTRATOR . '/templates/' . trim($templateFolder); // We dont want index.html etc... if (!is_dir($f) && !is_dir($a) || ($templateFolder == '.' || $templateFolder == '..')) { continue; } if (is_dir($a)) { $client_id = 1; } else { $client_id = 0; } // make it look like we want like Joomla 2.5+ would $template = array('type' => 'template', 'template' => $templateFolder, 'client_id' => $client_id, 'enabled' => 1); // Convert to an obj $templates[] = json_decode(json_encode($template)); } // Merge all the "extensions" we have found all over the place $installedExtensions = array_merge($components, $plugins, $modules, $templates); } $lang = JFactory::getLanguage(); // Load all the language strings up front incase any strings are shared foreach ($installedExtensions as $k => $ext) { $lang->load(strtolower($ext->element) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->element) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($ext->name) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->name) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($ext->title) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->title) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($ext->element), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->element), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($ext->name), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->name), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($ext->title), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($ext->title), JPATH_SITE, 'en-GB', TRUE); $element = str_replace('_TITLE', '', strtoupper($ext->element)); $name = str_replace('_TITLE', '', strtoupper($ext->name)); $title = str_replace('_TITLE', '', strtoupper($ext->title)); $lang->load(strtolower($element) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($element) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($name) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($name) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($title) . ".sys", JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($title) . ".sys", JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($element), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($element), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($name), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($name), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($title), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load(strtolower($title), JPATH_SITE, 'en-GB', TRUE); // templates $lang->load('tpl_' . strtolower($name), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load('tpl_' . strtolower($name), JPATH_SITE, 'en-GB', TRUE); // Joomla 1.5.x modules $lang->load('mod_' . strtolower($name), JPATH_ADMINISTRATOR, 'en-GB', TRUE); $lang->load('mod_' . strtolower($name), JPATH_SITE, 'en-GB', TRUE); // tut tut Akeeba - bad naming! $lang->load(strtolower('PLG_SYSTEM_SRP'), JPATH_ADMINISTRATOR, 'en-GB', TRUE); // should be plg_srp $lang->load(strtolower('PLG_SYSTEM_ONECLICKACTION'), JPATH_SITE, 'en-GB', TRUE); // should be plg_oneclickaction $lang->load(strtolower('PLG_SYSTEM_ONECLICKACTION'), JPATH_ADMINISTRATOR, 'en-GB', TRUE); // should be plg_oneclickaction // Joomla 1.5 plugins if ($ext->type == 'plugin') { $plg = 'plg_' . $ext->folder . '_' . $ext->element; $lang->load(strtolower($plg), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($plg), JPATH_ADMINISTRATOR, 'en-GB', TRUE); } if ($ext->type == 'template') { $plg = 'tpl_' . $ext->name; $lang->load(strtolower($plg), JPATH_SITE, 'en-GB', TRUE); $lang->load(strtolower($plg), JPATH_ADMINISTRATOR, 'en-GB', TRUE); } } // ok now we have the extensions - get the xml for further offline crunching foreach ($installedExtensions as $k => $ext) { // remove not supported types :-( if ($ext->type == 'file' || $ext->type == 'package') { unset($installedExtensions[$k]); continue; } $ext->xmlFile = $this->findManifest($ext); try { if ($ext->xmlFile !== FALSE) { $parts = explode('/', $ext->xmlFile); array_pop($parts); $ext->path = implode('/', $parts); bfLog::log('Loading XML file = ' . str_replace(JPATH_BASE, '', $ext->xmlFile)); $xml = trim(file_get_contents($ext->xmlFile)); $myXML = new SimpleXMLElement($xml); if (property_exists($myXML, 'description')) { $ext->desc = $myXML->description; } $ext->xmlFileContents = base64_encode(gzcompress($xml)); $ext->xmlFileCreated = filemtime($ext->xmlFile); } else { $ext->MANIFESTERROR = TRUE; } } catch (Exception $e) { bfLog::log('EXCEPTION = ' . $ext->xmlFile . ' ' . $e->getMessage()); die('Could not process XML file at: ' . str_replace(JPATH_BASE, '', $ext->xmlFile)); } $ext->name = JText::_($ext->name); $ext->title = JText::_($ext->title); $ext->desc = base64_encode(gzcompress(JText::_($ext->desc))); // remove base paths - we dont want to leak data :) $ext->xmlFile = $this->removeBase($ext->xmlFile); $ext->path = $this->removeBase($ext->path); // Sort so its pretty - not that anyone sees, but debugging is easier $ext = (array) $ext; ksort($ext); // push to the result $installedExtensions[$k] = $ext; } return json_encode($installedExtensions); }
/** * */ public static function truncate() { bflog::checkPermissions(); @unlink('tmp/log.tmp'); bfLog::log('Log file truncated'); // populate the config into the log bfLog::log('PHP Max Memory = ' . ini_get('memory_limit')); bfLog::log('PHP ini_setted Max Time = ' . ini_get('max_execution_time')); bfLog::log('PHP bfTimer Max Time = ' . bfTimer::getInstance()->getMaxTime()); }
private function clearTmpFiles() { require 'bfFilesystem.php'; $filesAndFolders = Bf_Filesystem::readDirectory(JPATH_ROOT . '/tmp', '.', true); foreach ($filesAndFolders as $pointer) { $pointer = JPATH_ROOT . '/tmp/' . $pointer; if (is_dir($pointer)) { bfLog::log('Deleting ' . $pointer); Bf_Filesystem::deleteRecursive($pointer, TRUE); } else { bfLog::log('Deleting ' . $pointer); unlink($pointer); } } file_put_contents(JPATH_ROOT . '/tmp/index.html', '<html><body bgcolor="#FFFFFF"></body></html> '); $sql = 'DELETE FROM bf_files WHERE filewithpath LIKE "/tmp%" AND filewithpath != "/tmp/index.html"'; $this->_db->setQuery($sql); $this->_db->query(); bfEncrypt::reply('success', array('res' => TRUE)); }
* * bfNetwork is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bfNetwork is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package. If not, see http://www.gnu.org/licenses/ */ // Decrypt or die require 'bfEncrypt.php'; /** * If we have got here then we have already passed through decrypting * the encrypted header and so we are sure we are now secure and no one * else cannot run the code below..... * ... ... */ // Get the steps require 'bfStep.php'; // Get the gutsy auditor tool require 'bfAuditor.php'; // Tick over... inject the decrypted object $scanner = new bfAudit($dataObj); // Tick Tock... bfLog::log('Tick'); $scanner->tick();
/** * Find out some basic information about this site and its setup */ private function bestpracticesecurityAction() { bfLog::log('============================================='); bfLog::log('=========bestpracticesecurityAction=========='); bfLog::log('============================================='); $app = JFactory::getApplication('site'); $this->platform = 'Joomla'; // Mark PHP in places PHP should not be! if ($ids = $this->_phpInWrongPlaces()) { $this->db->setQuery("UPDATE bf_files SET `suspectcontent` = 1 WHERE id IN (" . implode(',', $ids) . ")"); $this->db->query(); } // Remove OUR stuff as we dont need to report on that $this->db->setQuery("DELETE FROM bf_files WHERE\n\t\t\t\tfilewithpath = '/plugins/system/j15_bfnetwork.xml'\n\t\t\t\tOR filewithpath = '/plugins/system/j25_30_bfnetwork.xml'\n\t\t\t\tOR filewithpath LIKE '/plugins/system/bfnetwork%'"); $this->db->query(); // Remove OUR stuff as we dont need to report on that $this->db->setQuery("DELETE FROM bf_folders WHERE folderwithpath LIKE '/plugins/system/bfnetwork%'"); $this->db->query(); // Report count of all .htaccess files $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%.htaccess"'); $this->htaccess_files = $this->db->LoadResult(); // Report count of all files with 777 permissions $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE fileperms LIKE "%777%"'); $this->files_777 = $this->db->LoadResult(); // Report count of all folders with 777 permissions $this->db->setQuery('SELECT COUNT(*) FROM bf_folders WHERE folderinfo LIKE "%777%"'); $this->folders_777 = $this->db->LoadResult(); // Report all hidden folders like .git or .svn $this->db->setQuery('SELECT COUNT(*) FROM bf_folders WHERE folderwithpath LIKE "%/.%"'); $this->hidden_folders = $this->db->LoadResult(); // Report all hidden files like .htaccess .hack $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%/.%"'); $this->hidden_files = $this->db->LoadResult(); // Report nested Joomla versions $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%/administrator/index.php"'); $this->nestedinstalls = $this->db->LoadResult(); // Report file what might have been renamed to hide $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%.old%" OR filewithpath LIKE "%.bak%" OR filewithpath LIKE "%.backup%"'); $this->renamedtohidefiles = $this->db->LoadResult(); // Report any error_log files $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%error_log"'); $this->error_logs_seen = $this->db->LoadResult(); // Report files in the /tmp folder $this->db->setQuery('SELECT count(*) FROM bf_files WHERE filewithpath LIKE "/tmp%" AND filewithpath != "/tmp/index.html"'); $this->tmp_install_folders = $this->db->LoadResult(); // Report any encrypted files $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE encrypted = 1'); $this->encrypted_files = $this->db->LoadResult(); // Report suspect files $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE suspectcontent = 1'); $this->suspectfiles = $this->db->LoadResult(); $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%php.ini%"'); $this->phpiniseen = $this->db->LoadResult(); // has_robots_modified $sql = 'SELECT core.hash AS corehash, currenthash AS currenthash FROM bf_core_hashes AS core LEFT JOIN bf_files AS f ON f.filewithpath = core.filewithpath WHERE core.filewithpath = "/%s" OR core.filewithpath = "/%s" LIMIT 1'; $this->db->setQuery(sprintf($sql, 'robots.txt', 'robots.txt.dist')); $row = $this->db->loadAssocList(); if ($row) { if ($row[0]['corehash'] === $row[0]['currenthash']) { $this->has_robots_modified = 0; } else { $this->has_robots_modified = 1; } } else { $this->has_robots_modified = 0; } // Files over 2Mb $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE SIZE > 2097152'); $this->large_files = $this->db->LoadResult(); // Doing this again here now that I refactored the audit for perfomance I now need to do this much later on :-( $this->hashfailedcount = $this->gethashfailurecountAction(TRUE); // Report if we have default user ids $this->user_hasdefaultuserids = $this->_hasDefaultUserids(); // PhP in wrong places $phpInWrongPlaces = $this->_phpInWrongPlaces(); $this->phpinwrongplace = $phpInWrongPlaces ? count($phpInWrongPlaces) : 0; /** * @todo add these http://en.wikipedia.org/wiki/List_of_archive_formats */ // Report all archives $this->db->setQuery('SELECT COUNT(*) FROM bf_files WHERE filewithpath LIKE "%.zip" OR filewithpath LIKE "%.tar" OR filewithpath LIKE "%.tar.gz" OR filewithpath LIKE "%.bz2" OR filewithpath LIKE "%.gzip" OR filewithpath LIKE "%.bzip2"'); $this->archive_files = $this->db->LoadResult(); // ok got this far, toggle to the next step AND sleep $this->nextStepPlease(TRUE); }
/** * Encrypt a string using the RC4 Key provided in the encrypted request * from the service backend * * @param string $msg * * @return string Base64encoded message */ public static function getEncrypted($msg) { $start = time(); bfLog::log('Starting Encryption....'); // check our msg is a string if (is_object($msg) || is_array($msg)) { $msg = json_encode($msg); } // init a RC4 encryption routine - MUCH faster than public/private key $rc4 = new Crypt_RC4(); if (!defined('RC4_KEY')) { bfLog::log('NO RC4_KEY FOUND!!'); die('No Encryption Key'); } // Use the one time encryption key the requester provided $rc4->setKey(RC4_KEY); // encrypt the data $encrypted = $rc4->encrypt($msg); // return the data, encoded just in case $str = base64_encode($encrypted); bfLog::log('Finished Encryption.... took ' . (time() - $start) . ' seconds'); return $str; }
/** * Purges the Joomla! update cache. We ARE NOT using this cache, but the CMS * does. We want to bust the cache to provent Joomla! from reporting updates * after we install an update through our component * * @return bool True on success */ public function purgeJoomlaUpdateCache() { bfLog::log('running purgeJoomlaUpdateCache'); $db = JFactory::getDbo(); // Modify the database record $update_site = new stdClass(); $update_site->last_check_timestamp = 0; $update_site->enabled = 1; $update_site->update_site_id = 1; $db->updateObject('#__update_sites', $update_site, 'update_site_id'); $query = $db->getQuery(TRUE)->delete($db->quoteName('#__updates'))->where($db->quoteName('update_site_id') . ' = ' . $db->quote('1')); $db->setQuery($query); if (method_exists($db, 'execute')) { if ($db->execute()) { return TRUE; } else { return FALSE; } } else { if ($db->query()) { return TRUE; } else { return FALSE; } } }