/**
* AUTOCLEANER - DIRECT DELETE VERSION
* Hati-hati: langsung menghapus file yang terdeteksi berbahaya (tanpa karantina).
* BACKUP dulu. HAPUS script ini setelah selesai.
*/
// ========== CONFIG ==========
$dryRun = false; // true = simulasi saja; false = HAPUS nyata
$aggressive = true; // true = hapus semua PHP non-whitelist; false = hanya yang ter-flag
$logFile = __DIR__ . DIRECTORY_SEPARATOR . 'cleaner.log';
$whitelist = [
'index.php',
'index.html',
'sugestionsfree.php',
'121212122dir.php',
't12155454est.php',
basename(__FILE__),
basename($logFile),
];
$scanReadBytes = 65536; // bytes to read when scanning
$smallFileThreshold = 300; // bytes considered "small"
$recentWindow = 7 * 24 * 3600; // 7 days
// ========== HELPERS ==========
function logMsg($msg) {
global $logFile;
$line = date('Y-m-d H:i:s') . " - " . $msg . PHP_EOL;
@file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
echo htmlspecialchars($msg) . "
";
}
function normalizeWhitelist($arr) {
$out = [];
foreach ($arr as $i) $out[] = strtolower(trim(basename($i)));
return $out;
}
$whitelist = normalizeWhitelist($whitelist);
function isUnderWebroot($target) {
$root = realpath(__DIR__);
$t = @realpath($target);
return $t && strpos($t, $root) === 0;
}
function isWhitelisted($filename) {
global $whitelist;
return in_array(strtolower(basename($filename)), $whitelist, true);
}
function isSuspiciousContent($content) {
$patterns = [
'/eval\s*\(/i',
'/base64_decode\s*\(/i',
'/gzinflate\s*\(/i',
'/gzuncompress\s*\(/i',
'/shell_exec\s*\(/i',
'/proc_open\s*\(/i',
'/popen\s*\(/i',
'/passthru\s*\(/i',
'/system\s*\(/i',
'/exec\s*\(/i',
'/preg_replace\s*\([^)]*e[^)]*\)/i',
'/create_function\s*\(/i',
'/(str_rot13|strrev)\s*\(/i',
'/\$_(GET|POST|REQUEST|COOKIE)\[/i',
'/<\?php\s*\$GLOBALS\[.*\]/i',
'/\\\\x[0-9a-f]{2}/i',
'/\\\\u[0-9a-f]{4}/i',
'/base64_[a-z0-9_]+/i',
'/[A-Za-z0-9+\/]{100,}={0,2}/' // long base64 strings
];
foreach ($patterns as $p) {
if (@preg_match($p, $content)) return true;
}
return false;
}
function fileLooksSuspicious($path) {
global $scanReadBytes, $smallFileThreshold, $recentWindow;
if (!is_file($path) || !is_readable($path)) return false;
$name = basename($path);
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (!in_array($ext, ['php','phtml','php5','phar'])) return false;
$size = @filesize($path);
$content = @file_get_contents($path, false, null, 0, $scanReadBytes);
if ($content === false) return false;
if (isSuspiciousContent($content)) return true;
if ($size > 0 && $size < $smallFileThreshold && preg_match('/\b(eval|base64_decode|gzinflate|shell_exec|system|exec)\b/i', $content)) return true;
if (preg_match('/^[a-z0-9\-_]{8,}\.(php|phtml)$/i', $name)) return true;
if ((time() - @filemtime($path)) < $recentWindow && preg_match('/\$_(GET|POST|REQUEST|COOKIE)\[/i', $content)) return true;
return false;
}
// ========== RECURSIVE CLEANER (DIRECT DELETE) ==========
function cleanRecursiveDelete($dir, $dryRun=false, $aggressive=true, &$stats=null) {
if ($stats === null) $stats = ['scanned'=>0,'deleted'=>0,'kept'=>0,'errors'=>0,'dirsRemoved'=>0];
$files = @scandir($dir);
if ($files === false) { logMsg("[ERROR] cannot open dir: $dir"); $stats['errors']++; return $stats; }
foreach ($files as $file) {
if ($file === '.' || $file === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $file;
// Security: operate only under webroot
if (!isUnderWebroot($path)) { logMsg("[SKIP] $path (outside webroot)"); $stats['kept']++; continue; }
// Keep whitelist
if (isWhitelisted($file)) { logMsg("[KEEP] $path (whitelist)"); $stats['kept']++; continue; }
// symlink
if (is_link($path)) {
logMsg("[SYMLINK] $path");
if (!$dryRun) {
if (@unlink($path)) { logMsg("[DELETE SYMLINK] $path"); $stats['deleted']++; }
else { logMsg("[ERROR] failed unlink symlink: $path"); $stats['errors']++; }
}
continue;
}
// directory: recurse
if (is_dir($path)) {
cleanRecursiveDelete($path, $dryRun, $aggressive, $stats);
// remove empty dir if safe
$rem = @scandir($path);
if ($rem !== false && count($rem) <= 2) {
if (!$dryRun) {
if (@rmdir($path)) { logMsg("[DELETE DIR] $path"); $stats['dirsRemoved']++; }
else { logMsg("[ERROR] failed remove dir: $path"); $stats['errors']++; }
} else {
logMsg("[DRY-RUN] Would remove empty dir: $path");
}
} else {
logMsg("[KEEP DIR] $path (not empty)");
}
continue;
}
// file
$stats['scanned']++;
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
$isPhpLike = in_array($ext, ['php','phtml','php5','phar']);
$flagged = false;
$reason = '';
if ($isPhpLike) {
if (fileLooksSuspicious($path)) { $flagged = true; $reason = 'heuristic-suspicious'; }
elseif ($aggressive) { $flagged = true; $reason = 'aggressive-php'; }
} else {
if ($aggressive && in_array($ext, ['inc','bak','phps','pht'])) { $flagged = true; $reason = 'aggressive-other'; }
}
if ($flagged) {
if ($dryRun) {
logMsg("[DRY-RUN] Would delete ($reason): $path");
} else {
if (@unlink($path)) {
logMsg("[DELETE FILE] $path ($reason)");
$stats['deleted']++;
} else {
logMsg("[ERROR] Failed delete: $path ($reason)");
$stats['errors']++;
}
}
} else {
logMsg("[KEEP FILE] $path (not suspicious)");
$stats['kept']++;
}
}
return $stats;
}
// ========== EXECUTE ==========
echo "Autocleaner - Direct Delete
";
echo "<p><strong>WARNING:</strong> This will " . ($dryRun ? "only simulate (dry-run)" : "DELETE files") . " if run now.</p>";
logMsg("=== START AUTOCLEANER (DIRECT DELETE) at " . __DIR__ . " ===");
logMsg("CONFIG: dryRun=" . ($dryRun ? 'true' : 'false') . ", aggressive=" . ($aggressive ? 'true' : 'false'));
$stats = cleanRecursiveDelete(__DIR__, $dryRun, $aggressive);
logMsg("=== FINISH AUTOCLEANER ===");
logMsg("STATS: scanned={$stats['scanned']} deleted={$stats['deleted']} kept={$stats['kept']} dirsRemoved={$stats['dirsRemoved']} errors={$stats['errors']}");
echo "Ringkasan
";
echo "<ul>";
echo "<li>Scanned files: " . intval($stats['scanned']) . "</li>";
echo "<li>Files deleted: " . intval($stats['deleted']) . "</li>";
echo "<li>Files kept: " . intval($stats['kept']) . "</li>";
echo "<li>Dirs removed: " . intval($stats['dirsRemoved']) . "</li>";
echo "<li>Errors: " . intval($stats['errors']) . "</li>";
echo "</ul>";
echo "<p>Log: <code>" . htmlspecialchars(basename($logFile)) . "</code></p>";
echo "<p><strong>Hapus file ini setelah selesai!</strong></p>";
// show tail log (200 lines)
$log = @file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($log) {
$tail = array_slice($log, -200);
echo "<h4>Log (tail 200)</h4><textarea style='width:100%;height:300px'>" . htmlspecialchars(implode("\n", $tail)) . "</textarea>";
}