'/\bexec\s*\(/i', 'gzinflate_call' => '/gzinflate\s*\(/i', 'file_put_contents'=> '/file_put_contents\s*\(/i', 'file_get_contents'=> '/file_get_contents\s*\(/i', 'system_call' => '/\bsystem\s*\(/i', 'passthru_call' => '/\bpassthru\s*\(/i', 'shell_exec_call' => '/\bshell_exec\s*\(/i', 'move_uploaded' => '/move_uploaded_file\s*\(/i', 'eval_call' => '/\beval\s*\(/i', 'base64_mention' => '/base64_decode|base64_encode/i', 'preg_replace_e' => '/preg_replace\s*\([\s\S]*?\/e[\'"]/i', 'create_function' => '/create_function\s*\(/i', 'assert_call' => '/\bassert\s*\(/i', 'backtick_exec' => '/`[^`]+`/', 'hex_obfuscation' => '/(\\\\x[0-9a-fA-F]{2}){3,}/', // \xNN sequences 'long_single_line' => '/^[^\n]{1000,}$/m', ]; // filename heuristics $SUSPICIOUS_NAME_PATTERNS = [ '/\b(shell|backdoor|r57|c99|weevely|wso|phpbackdoor|passthru|cmd)\b/i', '/\.bak$/i', '/\.old$/i', '/\.swp$/i' ]; // ---------------- helpers ---------------- function is_text_file($path) { $fp = @fopen($path, 'rb'); if (!$fp) return false; $chunk = fread($fp, 8192); fclose($fp); return (strpos($chunk, "\0") === false); } function preg_grep_array($patterns, $subject) { foreach ($patterns as $p) { if (@preg_match($p, $subject)) return true; } return false; } function is_js_or_html_path($path) { $p = strtolower($path); foreach (['.min.js', '.js', '.html', '.htm'] as $ext) { if (substr($p, -strlen($ext)) === $ext) return true; } return false; } function quarantine_file($path) { $rp = realpath($path); if (!$rp) return false; $stamp = date('Ymd_His'); $hash = substr(hash('sha256', $rp . microtime(true)), 0, 10); $dest = QUARANTINE_DIR . "/{$stamp}_{$hash}_" . basename($rp); if (@copy($rp, $dest)) { @chmod($dest, 0600); return $dest; } return false; } function delete_file_with_backup($path) { if (!is_file($path)) return [false, null]; $backup = quarantine_file($path); $ok = @unlink($path); return [$ok, $backup]; } function save_detected($file) { // append to raw list for manual review @file_put_contents(SAVE_RAW_LIST, $file . PHP_EOL, FILE_APPEND | LOCK_EX); } function sha256_file_or_empty($path) { return is_file($path) ? @hash_file('sha256', $path) : ''; } // ---------------- scan single file content ---------------- function scan_file_content_for_signs($file_location, $content_patterns, $name_patterns) { $results = []; if (!is_readable($file_location)) return $results; $size = @filesize($file_location); if ($size === false || $size > MAX_FILE_SIZE_BYTES) return $results; if (!is_text_file($file_location)) return $results; $contents = @file_get_contents($file_location); if ($contents === false || strlen($contents) === 0) return $results; $lower = $contents; // patterns already case-insensitive foreach ($content_patterns as $key => $rx) { if (@preg_match($rx, $lower, $m)) { $results[] = [ 'type' => 'suspicious_content', 'pattern' => $key, 'path' => $file_location, 'snippet' => substr(str_replace("\n", " ", $m[0] ?? ''), 0, 300), 'sha256' => sha256_file_or_empty($file_location) ]; } } // filename heuristics $basename = basename($file_location); foreach ($name_patterns as $rx) { if (@preg_match($rx, $basename)) { $results[] = [ 'type' => 'suspicious_name', 'pattern' => $rx, 'path' => $file_location, 'sha256' => sha256_file_or_empty($file_location) ]; } } return $results; } // ---------------- scan directory tree (server-side files only) ---------------- function scan_local_tree_with_content($root, $content_patterns, $name_patterns) { $results = []; $count = 0; try { $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)); } catch (Exception $e) { return [['type'=>'error','path'=>$root,'error'=>'Cannot open dir: '.$e->getMessage()]]; } foreach ($it as $fileinfo) { if (!$fileinfo->isFile()) continue; if (++$count > MAX_LOCAL_FILES) break; $path = $fileinfo->getPathname(); $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); // allowed server-side extensions (skip js/html) $allowed_exts = ['php','phtml','php3','php4','php5','php7','inc','cgi','pl','asp','aspx','jsp']; $basename = $fileinfo->getFilename(); $shouldScan = in_array($ext, $allowed_exts) || preg_grep_array($name_patterns, $basename); if (!$shouldScan) continue; // scan content for suspicious patterns $found = scan_file_content_for_signs($path, $content_patterns, $name_patterns); if (!empty($found)) { foreach ($found as $f) $results[] = $f; } } return $results; } // ---------------- result writing helpers ---------------- function write_results_to_resulttxt($findings) { // Each line: [type] pattern -> /path $lines = []; foreach ($findings as $f) { $type = strtoupper($f['type'] ?? 'UNKNOWN'); $pat = $f['pattern'] ?? ''; $path = $f['path'] ?? ($f['url'] ?? ''); $lines[] = "[$type] $pat -> $path"; } @file_put_contents(RESULT_TXT, implode(PHP_EOL, $lines) . PHP_EOL, LOCK_EX); } // ---------------- handle actions ---------------- $feedback = ''; $view_content = ''; $view_meta = null; if (isset($_GET['kill'])) { // Self-delete (dangerous) $self = __FILE__; @unlink($self); echo "Self-delete attempted. If file remains, check permissions."; exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = $_POST['action'] ?? ''; if ($action === 'scan_local') { $root = trim($_POST['local_root'] ?? ''); if ($root === '' || !is_dir($root)) { $feedback = "Direktori tidak valid: " . htmlspecialchars($root); } else { // run content-based scan $findings = scan_local_tree_with_content($root, $SUSPICIOUS_CONTENT_PATTERNS, $SUSPICIOUS_NAME_PATTERNS); // also convert to lines and save write_results_to_resulttxt($findings); // save raw list for manual review foreach ($findings as $f) { save_detected($f['path'] ?? ''); } $feedback = "Scan selesai. Ditemukan: " . count($findings) . " item. Hasil disimpan di result.txt"; } } elseif ($action === 'view_file') { $path = $_POST['file_path'] ?? ''; if ($path && is_file($path) && is_readable($path)) { if (is_js_or_html_path($path)) { $feedback = "Viewing disabled for .js/.html per configuration."; } else { $raw = @file_get_contents($path); if ($raw === false) $feedback = "Gagal membaca file."; else { $view_content = htmlspecialchars(mb_substr($raw, 0, 20000)); $view_meta = ['path'=>$path,'size'=>strlen($raw),'modified'=>@date('c',@filemtime($path))]; } } } else $feedback = "File tidak ditemukan atau tidak bisa dibaca."; } elseif ($action === 'delete_file') { $path = $_POST['file_path'] ?? ''; if ($path && is_file($path)) { if (is_js_or_html_path($path)) { $feedback = "Deletion disabled for .js/.html per configuration."; } else { list($ok,$backup) = delete_file_with_backup($path); if ($ok) $feedback = "File dihapus: ".htmlspecialchars($path).". Backup: ".($backup?htmlspecialchars(basename($backup)):'(none)'); else $feedback = "Gagal menghapus file: ".htmlspecialchars($path); // refresh result.txt best-effort if (file_exists(RESULT_TXT)) { $lines = file(RESULT_TXT, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $new = []; foreach ($lines as $ln) if (strpos($ln, $path) === false) $new[] = $ln; file_put_contents(RESULT_TXT, implode(PHP_EOL,$new).PHP_EOL); } } } else $feedback = "File tidak ditemukan."; } elseif ($action === 'clear_results') { @file_put_contents(RESULT_TXT, ''); $feedback = "result.txt dikosongkan."; } } // ---------------- read result.txt and filter .js/.html ---------------- $display_findings = []; if (file_exists(RESULT_TXT)) { $lines = file(RESULT_TXT, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $ln) { if (preg_match('/^\[(.+?)\]\s+(.+?)\s*->\s*(.+)$/', $ln, $m)) { $type = $m[1]; $pat = $m[2]; $path = $m[3]; if (is_js_or_html_path($path)) continue; // filter out old js/html results $display_findings[] = ['type'=>$type,'pattern'=>$pat,'path'=>$path]; } else { // fallback: if line contains a path skip js/html if (is_js_or_html_path($ln)) continue; $display_findings[] = ['type'=>'raw','pattern'=>'','path'=>$ln]; } } } // ---------------- render UI ---------------- $self = htmlspecialchars($_SERVER['PHP_SELF']); ?>
This scanner searches file contents for suspicious patterns (exec/gzinflate/eval/base64/etc). It skips .js/.html for actions. Use Clear results before re-scan to remove old entries.
| # | Type | Pattern | Path | Actions |
|---|---|---|---|---|
| = $i+1 ?> | = htmlspecialchars($f['type']) ?> | = htmlspecialchars($f['pattern']) ?> | = htmlspecialchars($f['path']) ?> | (file not present) |
No findings in result.txt (after filtering .js/.html). Run a content scan to detect suspicious files.
= $view_content ?>