/** * List all files inside wp-content that have been modified in the last days. * * @return void */ function sucuriscan_modified_files() { $valid_day_ranges = array(1, 3, 7, 30, 60); $template_variables = array('ModifiedFiles.List' => '', 'ModifiedFiles.SelectOptions' => '', 'ModifiedFiles.NoFilesVisibility' => 'visible', 'ModifiedFiles.DisabledVisibility' => 'hidden', 'ModifiedFiles.Days' => 0); // Find files modified in the last days. $back_days = SucuriScanRequest::post(':last_days', '[0-9]+'); if ($back_days !== false) { if ($back_days <= 0) { $back_days = 1; } elseif ($back_days >= 60) { $back_days = 60; } } else { $back_days = 7; } // Fix data type for the back days variable. $back_days = intval($back_days); $template_variables['ModifiedFiles.Days'] = $back_days; // Generate the options for the select field of the page form. foreach ($valid_day_ranges as $day) { $selected_option = $back_days == $day ? 'selected="selected"' : ''; $template_variables['ModifiedFiles.SelectOptions'] .= sprintf('<option value="%d" %s>%d</option>', $day, $selected_option, $day); } // The scanner for modified files can be disabled from the settings page. if (SucuriScanOption::is_enabled(':scan_modfiles')) { // Search modified files among the project's files. $content_hashes = sucuriscan_get_integrity_tree(WP_CONTENT_DIR, true); if (!empty($content_hashes)) { $back_days = current_time('timestamp') - $back_days * 86400; $counter = 0; foreach ($content_hashes as $file_path => $file_info) { if (isset($file_info['modified_at']) && $file_info['modified_at'] >= $back_days) { $css_class = $counter % 2 == 0 ? '' : 'alternate'; $mod_date = SucuriScan::datetime($file_info['modified_at']); $template_variables['ModifiedFiles.List'] .= SucuriScanTemplate::get_snippet('integrity-modifiedfiles', array('ModifiedFiles.CssClass' => $css_class, 'ModifiedFiles.CheckSum' => $file_info['checksum'], 'ModifiedFiles.FilePath' => SucuriScan::escape($file_path), 'ModifiedFiles.DateTime' => $mod_date, 'ModifiedFiles.FileSize' => $file_info['filesize'], 'ModifiedFiles.FileSizeHuman' => SucuriScan::human_filesize($file_info['filesize']), 'ModifiedFiles.FileSizeNumber' => number_format($file_info['filesize']))); $counter += 1; } } if ($counter > 0) { $template_variables['ModifiedFiles.NoFilesVisibility'] = 'hidden'; } } } else { $template_variables['ModifiedFiles.DisabledVisibility'] = 'visible'; } return SucuriScanTemplate::get_section('integrity-modifiedfiles', $template_variables); }
/** * Check whether the core WordPress files where modified, removed or if any file * was added to the core folders. This function returns an associative array with * these keys: * * <ul> * <li>modified: Files with a different checksum according to the official WordPress archives,</li> * <li>stable: Files with the same checksums than the official files,</li> * <li>removed: Official files which are not present in the local project,</li> * <li>added: Files present in the local project but not in the official WordPress packages.</li> * </ul> * * @param integer $version Valid version number of the WordPress project. * @return array Associative array with these keys: modified, stable, removed, added. */ function sucuriscan_check_core_integrity($version = 0) { $latest_hashes = SucuriScanAPI::getOfficialChecksums($version); $base_content_dir = defined('WP_CONTENT_DIR') ? basename(rtrim(WP_CONTENT_DIR, '/')) : ''; if (!$latest_hashes) { return false; } $output = array('added' => array(), 'removed' => array(), 'modified' => array(), 'stable' => array()); // Get current filesystem tree. $wp_top_hashes = sucuriscan_get_integrity_tree(ABSPATH, false); $wp_admin_hashes = sucuriscan_get_integrity_tree(ABSPATH . 'wp-admin', true); $wp_includes_hashes = sucuriscan_get_integrity_tree(ABSPATH . 'wp-includes', true); $wp_core_hashes = array_merge($wp_top_hashes, $wp_admin_hashes, $wp_includes_hashes); // Compare remote and local checksums and search removed files. foreach ($latest_hashes as $file_path => $remote_checksum) { if (sucuriscan_ignore_integrity_filepath($file_path)) { continue; } $full_filepath = sprintf('%s/%s', ABSPATH, $file_path); // Patch for custom content directory path. if (!file_exists($full_filepath) && strpos($file_path, 'wp-content') !== false && defined('WP_CONTENT_DIR')) { $file_path = str_replace('wp-content', $base_content_dir, $file_path); $dir_content_dir = dirname(rtrim(WP_CONTENT_DIR, '/')); $full_filepath = sprintf('%s/%s', $dir_content_dir, $file_path); } // Check whether the official file exists or not. if (file_exists($full_filepath)) { $local_checksum = @md5_file($full_filepath); if ($local_checksum !== false && $local_checksum === $remote_checksum) { $output['stable'][] = array('filepath' => $file_path, 'is_fixable' => false, 'modified_at' => 0); } else { $modified_at = @filemtime($full_filepath); $is_fixable = (bool) is_writable($full_filepath); $output['modified'][] = array('filepath' => $file_path, 'is_fixable' => $is_fixable, 'modified_at' => $modified_at); } } else { $is_fixable = is_writable(dirname($full_filepath)); $output['removed'][] = array('filepath' => $file_path, 'is_fixable' => $is_fixable, 'modified_at' => 0); } } // Search added files (files not common in a normal wordpress installation). foreach ($wp_core_hashes as $file_path => $extra_info) { $file_path = str_replace(DIRECTORY_SEPARATOR, '/', $file_path); $file_path = @preg_replace('/^\\.\\/(.*)/', '$1', $file_path); if (sucuriscan_ignore_integrity_filepath($file_path)) { continue; } if (!array_key_exists($file_path, $latest_hashes)) { $full_filepath = ABSPATH . '/' . $file_path; $modified_at = @filemtime($full_filepath); $is_fixable = (bool) is_writable($full_filepath); $output['added'][] = array('filepath' => $file_path, 'is_fixable' => $is_fixable, 'modified_at' => $modified_at); } } return $output; }