File: /home/ignijdop/ceylonaddictiontours.com/wordpress_cleanup_cron.php
<?php
/**
* WordPress Full Security & Cleanup Script (Cron-safe, Duplicate-free)
* Features:
* - Root cleanup (keep WP core + .htaccess + logs + cron script)
* - Dangerous plugin removal ('wp-file-manager', 'file-manager-advanced')
* - PHP execution block in uploads
* - Permissions hardening
* - Malware scan including wp-config.php
* - WP-CLI restore (core files only)
* - JSON report + Email alert (even in dry-run)
*/
$root = __DIR__;
$dryRun = true; // default dry-run
$logFile = $root . '/cleanup.log';
$reportFile = $root . '/security-report.json';
$scriptFile = basename(__FILE__);
$cronScript = 'wordpress_cleanup_cron.php'; // Do not delete
$alertEmail = 'corn@ignitemv.com'; // CHANGE THIS
$dangerPlugins = ['wp-file-manager','file-manager-advanced'];
$rootDeleted = [];
$patterns = ['eval(','base64_decode','gzinflate','shell_exec','passthru','str_rot13'];
$safeFiles = [$cronScript]; // Files to ignore in malware scan
// CLI FLAG
foreach ($argv ?? [] as $arg) if ($arg==='--run') $dryRun=false;
// LOG FUNCTION
function log_msg($msg){
global $logFile;
file_put_contents($logFile,date('[Y-m-d H:i:s] ').$msg.PHP_EOL,FILE_APPEND);
}
// RECURSIVE DELETE
function rrmdir_or_delete($path){
if(!file_exists($path)) return;
if(is_dir($path)){
foreach(scandir($path) as $f){
if($f==='.'||$f==='..') continue;
rrmdir_or_delete($path.'/'.$f);
}
rmdir($path);
} else unlink($path);
}
// ---------------- STEP 0: ROOT CLEANUP ----------------
$coreKeep = [
'wp-admin','wp-content','wp-includes','index.php','license.txt','readme.html',
'wp-activate.php','wp-blog-header.php','wp-comments-post.php','wp-config-sample.php',
'wp-config.php','wp-cron.php','wp-links-opml.php','wp-load.php','wp-login.php',
'wp-mail.php','wp-settings.php','wp-signup.php','wp-trackback.php','xmlrpc.php',
'.htaccess',
// Keep cleanup script & cron script & logs
$scriptFile,
$cronScript,
basename($logFile),
basename($reportFile)
];
foreach(scandir($root) as $item){
if($item==='.'||$item==='..') continue;
if(in_array($item,$coreKeep)) continue;
$path=$root.'/'.$item;
$rootDeleted[]=$item;
if($dryRun) log_msg("DRY-RUN Root delete: {$item}");
else{ rrmdir_or_delete($path); log_msg("Deleted root item: {$item}"); }
}
// ---------------- STEP 1: FORCE index.php ----------------
$indexContent = "<?php\ndefine('WP_USE_THEMES', true);\nrequire __DIR__ . '/wp-blog-header.php';\n";
if(!$dryRun) file_put_contents($root.'/index.php',$indexContent);
log_msg("Checked index.php (forced WordPress loader)");
// ---------------- STEP 2: FORCE .htaccess ----------------
$htaccessContent = <<<HTACCESS
# ================================
# WordPress default rules
# ================================
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
</IfModule>
# ================================
# Security rules
# ================================
# Protect wp-config.php
<Files wp-config.php>
Order Allow,Deny
Deny from all
</Files>
# Protect .htaccess itself
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>
# Block access to readme/license/error files
<FilesMatch "^(readme\.html|license\.txt|error_log|wp-config-sample\.php)$">
Order Allow,Deny
Deny from all
</FilesMatch>
# Protect wp-content/uploads (no PHP execution)
<FilesMatch "\.php$">
<If "%{REQUEST_URI} =~ m#^/wp-content/uploads/#">
Require all denied
</If>
</FilesMatch>
# Optional: Block PHP in theme subfolders (only in subfolders with static files)
<FilesMatch "\.php$">
<If "%{REQUEST_URI} =~ m#^/wp-content/themes/your-theme/assets/#">
Require all denied
</If>
</FilesMatch>
# Block PHP in wp-content folder (except plugins and themes)
<IfModule mod_rewrite.c>
RewriteRule ^wp-content/.*\.(?:php[0-9]?|phtml)$ - [F,L]
</IfModule>
# Block direct access to wp-includes PHP
<IfModule mod_rewrite.c>
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php$ - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
# Block sensitive files
<FilesMatch "(^\.htaccess|\.htpasswd|php\.ini|install\.php|debug\.log|error_log|\.log$)">
Order Allow,Deny
Deny from all
</FilesMatch>
# Block author enumeration
<IfModule mod_rewrite.c>
RewriteCond %{QUERY_STRING} ^author=\d+ [NC]
RewriteRule .* - [F,L]
</IfModule>
# Block suspicious query strings
<IfModule mod_rewrite.c>
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
RewriteCond %{QUERY_STRING} (\.\./) [NC,OR]
RewriteCond %{QUERY_STRING} (etc/passwd|boot\.ini) [NC,OR]
RewriteCond %{QUERY_STRING} (eval\() [NC]
RewriteRule .* - [F,L]
</IfModule>
# Disable TRACE/TRACK
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F,L]
</IfModule>
# Disable directory listing
Options All -Indexes
# Remove header with PHP version
Header always unset X-Powered-By
Header unset X-Powered-By
# Additional security headers
<IfModule mod_headers.c>
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Header always set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
</IfModule>
# Block xmlrpc.php - prevents brute force and DDoS amplification attacks
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
</Files>
HTACCESS;
if(!$dryRun) file_put_contents($root.'/.htaccess', $htaccessContent);
log_msg(".htaccess updated with enhanced security rules");
// ---------------- STEP 3: BLOCK PHP IN UPLOADS ----------------
$uploadsHtaccess = $root.'/wp-content/uploads/.htaccess';
if(!$dryRun){
if(!is_dir(dirname($uploadsHtaccess))) mkdir(dirname($uploadsHtaccess),0755,true);
file_put_contents($uploadsHtaccess,"<FilesMatch \"\\.(php|php5|phtml|phar)$\">\nDeny from all\n</FilesMatch>");
}
log_msg("Uploads PHP execution blocked via .htaccess");
// ---------------- STEP 4: REMOVE DANGEROUS PLUGINS ----------------
$pluginDir = $root.'/wp-content/plugins';
foreach($dangerPlugins as $plugin){
$p = $pluginDir.'/'.$plugin;
if(file_exists($p)){
if($dryRun) log_msg("DRY-RUN Remove dangerous plugin: $plugin");
else{ rrmdir_or_delete($p); log_msg("Deleted dangerous plugin: $plugin"); }
}
}
// ---------------- STEP 5: PERMISSIONS ----------------
function fix_permissions($path){
if(is_dir($path)){
chmod($path,0755);
foreach(scandir($path) as $f) if($f!=='.'&&$f!=='..') fix_permissions("$path/$f");
} else chmod($path,0644);
}
if(!$dryRun){
fix_permissions($root);
if(file_exists($root.'/wp-config.php')) chmod($root.'/wp-config.php',0600);
}
log_msg("File & directory permissions hardened");
// ---------------- STEP 6: MALWARE SCAN ----------------
$suspicious=[];
$pluginSuspicious=[];
$configSuspicious=[];
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root));
foreach($rii as $file){
if($file->isDir()) continue;
if(pathinfo($file,PATHINFO_EXTENSION)!=='php') continue;
$content = @file($file);
foreach($content as $num=>$line){
foreach($patterns as $p){
if(stripos($line,$p)!==false){
$relPath = str_replace($root.'/','',$file);
if(str_starts_with($relPath,'wp-content/plugins/')){
$parts=explode('/',$relPath);
$pluginName = $parts[2]??'unknown';
$pluginSuspicious[$pluginName][]=['file'=>$relPath,'line_number'=>$num+1,'content'=>trim($line)];
} elseif($relPath==='wp-config.php'){
$configSuspicious[]=['line_number'=>$num+1,'content'=>trim($line)];
} elseif(!in_array($relPath,$safeFiles) && !in_array($relPath,$suspicious)) {
$suspicious[]=$relPath;
}
}
}
}
}
log_msg("Malware scan completed. Suspicious root files: ".count($suspicious)." , Plugins: ".count($pluginSuspicious)." , wp-config: ".count($configSuspicious));
// ---------------- STEP 7: WP-CLI ----------------
$wpCli = !empty(shell_exec('which wp 2>/dev/null'));
$wpVersion='Unknown';
$pluginList=[];
if($wpCli){
$wpVersion = trim(shell_exec('wp core version 2>/dev/null'));
$out = shell_exec('wp plugin list --format=json 2>/dev/null');
if($out) $pluginList = json_decode($out,true);
if(!$dryRun){
shell_exec('wp core verify-checksums --quiet');
shell_exec('wp core download --force --skip-content'); // keep wp-content safe
}
}
log_msg($wpCli ? "WP-CLI detected" : "WP-CLI not available");
// ---------------- STEP 8: JSON REPORT ----------------
$report=[
'timestamp'=>date('c'),
'dry_run'=>$dryRun,
'wordpress_version'=>$wpVersion,
'wp_cli'=>$wpCli,
'root_deleted_count'=>count($rootDeleted),
'root_deleted_items'=>$rootDeleted,
'suspicious_files_count'=>count($suspicious),
'suspicious_files'=>$suspicious,
'plugin_suspicious'=>$pluginSuspicious,
'wp_config_suspicious'=>$configSuspicious,
'installed_plugins'=>$pluginList
];
if(!$dryRun) file_put_contents($reportFile,json_encode($report,JSON_PRETTY_PRINT));
log_msg("Security JSON report generated");
// ---------------- STEP 9: EMAIL ALERT ----------------
$msg = "WP Security Cleanup Report\n\n";
$msg .= "Mode: ".($dryRun ? "DRY-RUN (No changes applied)" : "LIVE RUN")."\n\n";
$msg .= "WordPress Version: $wpVersion\n\n";
$msg .= "Root Deleted Files (".count($rootDeleted)."):\n";
foreach($rootDeleted as $f) $msg.=" - $f\n";
$msg .= "\nSuspicious Root PHP Files (".count($suspicious)."):\n";
foreach($suspicious as $f) $msg.=" - $f\n";
$msg .= "\nPLUGIN SUSPICIOUS DETAILS:\n";
foreach($pluginSuspicious as $pluginName=>$entries){
$msg.="\nPlugin: $pluginName\n";
foreach($entries as $e) $msg.=" - {$e['file']} [Line {$e['line_number']}]: {$e['content']}\n";
}
$msg .= "\nWP-CONFIG.PHP SUSPICIOUS LINES:\n";
foreach($configSuspicious as $c) $msg.=" - Line {$c['line_number']}: {$c['content']}\n";
mail(
$alertEmail,
'WP Security Cleanup Report - '.($dryRun ? 'DRY-RUN' : 'LIVE'),
$msg
);
log_msg("Security report email sent to {$alertEmail}");
// ---------------- END ----------------
echo "Cleanup completed\n";
if($dryRun) echo "DRY-RUN mode. Use --run to apply changes.\n";