收起左侧

给飞牛添加一个web的shell执行工具 root权限-作死版

11
回复
350
查看
[ 复制链接 ]

2

主题

2

回帖

0

牛值

江湖小虾

2025-7-1 14:13:41 显示全部楼层 阅读模式

给飞牛添加一个web的shell执行工具 root权限-作死版(图1)

给飞牛添加一个web的shell执行工具 root权限-作死版(图2)

给飞牛添加一个web的shell执行工具 root权限-作死版(图3)

给飞牛添加一个web的shell执行工具 root权限-作死版(图4)

这个代码实现了一个功能丰富的 Shell 命令执行工具,提供了命令执行、脚本管理、定时任务调度等功能。下面是对其核心功能的详细介绍:

主要功能模块

  1. **用户认证系统**
    • 支持用户名密码登录(默认用户:admin/admin123)
    • 基于会话的身份验证机制
    • 可配置是否需要认证
  2. **命令执行模块**
  • 提供文本框输入任意 Shell 命令
  • 实时显示命令执行结果和返回状态
  • 记录命令执行时间
  • 限制最大输出大小(1MB)防止内存溢出
  1. **脚本管理系统**

    • 保存常用命令为脚本文件
    • 加载已保存的脚本
    • 删除不需要的脚本
    • 显示脚本列表(按修改时间排序)
    • 支持上传和下载脚本文件
  2. **定时任务管理**

    • 基于 cron 表达式的定时任务设置
    • 提供常用 cron 预设模板(每分钟、每小时等)
    • 显示定时任务列表及状态
    • 计算并显示下次执行时间
    • 添加/删除定时任务
    • 检测任务运行状态(运行中/待执行)
  3. **日志与历史记录**

    • 记录所有命令执行日志
    • 显示最近 5 条执行历史
    • 日志包含时间、IP、用户、状态和命令内容
  4. **界面与用户体验**

    • 深色/浅色主题切换(保存用户偏好)
    • 响应式设计,适配不同屏幕尺寸
    • 命令文本框快捷操作(清空、全选)
    • 直观的视觉反馈(成功/失败状态提示)

安全特性

尽管代码中强调"完全取消命令限制",但仍包含以下安全措施:

  1. 基于会话的用户认证
  2. 脚本名称的正则表达式验证
  3. 日志记录所有操作
  4. 输出内容大小限制
  5. 定时任务添加时的格式验证
  6. 明确的安全警告提示

技术实现细节

  1. **后端技术**

    • 使用 PHP 实现所有功能
    • 通过 `exec()` 函数执行 Shell 命令
    • 使用 `crontab` 命令管理定时任务
    • 基于文件系统的脚本存储
  2. **前端技术**

    • 纯 HTML/CSS/JavaScript 实现
    • 原生 JavaScript 处理所有交互逻辑
    • 本地存储保存主题偏好
    • 表格展示数据列表
  3. **依赖管理**

    • 使用 Composer 管理第三方依赖
    • 支持 `mtdowling/cron-expression` 库解析 cron 表达式

这个工具非常适合开发和测试环境使用,但由于其高风险特性,强烈建议不要在生产环境或可公开访问的服务器上部署。其次这个工具需要安装 `mtdowling/cron-expression` 库来解析和计算 cron 表达式的下次执行时间。你可以通过 Composer 来安装这个依赖。

安装步骤

  1. 首先确保你的服务器上已经安装了 Composer。如果没有安装,可以按照 官方文档 进行安装。
  2. 在项目根目录下创建一个 `composer.json` 文件,内容如下:
{    "require": {        "mtdowling/cron-expression": "^1.3"    }}
  1. 在终端中执行以下命令安装依赖:
composer install

这将会在项目目录下创建一个 `vendor` 目录,并安装所需的库。

工具已有的自动加载支持

注意到代码中已经有自动加载 Composer 依赖的部分:

// 自动加载Composer依赖if (file_exists(__DIR__ . '/vendor/autoload.php')) {    require_once __DIR__ . '/vendor/autoload.php';}

所以只要按照上述步骤安装了库,工具就能正常使用 cron 表达式解析功能了。

功能说明

安装这个库后,定时任务管理模块将能够:

  1. 准确计算每个 cron 表达式的下次执行时间
  2. 在任务列表中显示状态信息(运行中/待执行)
  3. 提供更友好的定时任务配置体验

再次提醒,这个工具完全取消了命令限制,存在极高的安全风险,请务必仅在受信任的环境中使用。

<?php
// shell_executor.php
session_start();
date_default_timezone_set('Asia/Shanghai');
// 配置参数(完全取消命令限制,极度危险!)
$config = [
    'scripts_dir' => __DIR__ . '/scripts/', // 脚本存储目录
    'max_output_size' => 1024 * 1024, // 1MB
    'log_file' => __DIR__ . '/shell_execution.log',
    'require_auth' => true,
    'username' => 'admin',
    'password' => password_hash('admin123', PASSWORD_DEFAULT),
];
// 自动加载Composer依赖
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
    require_once __DIR__ . '/vendor/autoload.php';
}
// 创建脚本目录(如果不存在)
if (!file_exists($config['scripts_dir'])) {
    mkdir($config['scripts_dir'], 0755, true);
}
// 认证处理
if ($config['require_auth'] && !isset($_SESSION['authenticated'])) {
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'], $_POST['password'])) {
        if ($_POST['username'] === $config['username'] && password_verify($_POST['password'], $config['password'])) {
            $_SESSION['authenticated'] = true;
            header('Location: ' . $_SERVER['PHP_SELF']);
            exit;
        } else {
            $auth_error = '用户名或密码错误';
        }
    } else {
        ?>
<!DOCTYPE html>
<html>
<head>
    <title>Shell执行工具 - 登录</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 400px; margin: 50px auto; }
        .login-box { padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
        .error { color: red; margin-bottom: 10px; }
        input { width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }
        button { width: 100%; padding: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
    </style>
</head>
<body>
    <div class="login-box">
        <h2>Shell执行工具登录</h2>
        <?php if (isset($auth_error)): ?>
            <div class="error"><?php echo htmlspecialchars($auth_error); ?></div>
        <?php endif; ?>
        <form method="post">
            <input type="text" name="username" placeholder="用户名" required>
            <input type="password" name="password" placeholder="密码" required>
            <button type="submit">登录</button>
        </form>
    </div>
</body>
</html>
        <?php
        exit;
    }
}
// 记录日志
function log_action($command, $output, $success) {
    global $config;
    $log_entry = date('Y-m-d H:i:s') . ' - ' . 
                 (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'CLI') . ' - ' .
                 (isset($_SESSION['authenticated']) ? $_SESSION['username'] : 'guest') . ' - ' .
                 ($success ? 'SUCCESS' : 'ERROR') . ' - ' .
                 $command . PHP_EOL;
    // 限制日志中输出内容的大小
    $output = substr($output, 0, 1024);
    $log_entry .= "Output: " . $output . PHP_EOL . str_repeat('-', 50) . PHP_EOL;
    file_put_contents($config['log_file'], $log_entry, FILE_APPEND);
}
// 脚本管理功能
$script_message = '';
$scripts = [];
// 保存脚本
if (isset($_POST['save_script'])) {
    $script_name = trim($_POST['script_name']);
    $script_content = trim($_POST['command']);
    if (empty($script_name)) {
        $script_message = "错误: 请输入脚本名称";
    } elseif (preg_match('/[^a-zA-Z0-9_\-\.]/', $script_name)) {
        $script_message = "错误: 脚本名称只能包含字母、数字、下划线、连字符和点";
    } else {
        $script_path = $config['scripts_dir'] . $script_name . '.sh';
        if (file_put_contents($script_path, $script_content) !== false) {
            $script_message = "脚本已保存: $script_name.sh";
        } else {
            $script_message = "错误: 无法保存脚本";
        }
    }
}
// 删除脚本
if (isset($_GET['delete_script'])) {
    $script_name = basename($_GET['delete_script']);
    $script_path = $config['scripts_dir'] . $script_name;
    if (file_exists($script_path) && is_writable($script_path)) {
        unlink($script_path);
        $script_message = "脚本已删除: $script_name";
    } else {
        $script_message = "错误: 无法删除脚本";
    }
}
// 加载脚本
if (isset($_GET['load_script'])) {
    $script_name = basename($_GET['load_script']);
    $script_path = $config['scripts_dir'] . $script_name;
    if (file_exists($script_path) && is_readable($script_path)) {
        $_POST['command'] = file_get_contents($script_path);
    } else {
        $script_message = "错误: 无法加载脚本";
    }
}
// 获取脚本列表
if ($handle = opendir($config['scripts_dir'])) {
    while (false !== ($entry = readdir($handle))) {
        if ($entry != "." && $entry != ".." && pathinfo($entry, PATHINFO_EXTENSION) == 'sh') {
            $scripts[] = [
                'name' => pathinfo($entry, PATHINFO_FILENAME),
                'path' => $entry,
                'size' => filesize($config['scripts_dir'] . $entry),
                'mtime' => filemtime($config['scripts_dir'] . $entry)
            ];
        }
    }
    closedir($handle);
    // 按修改时间排序(最新的在前)
    usort($scripts, function($a, $b) {
        return $b['mtime'] - $a['mtime'];
    });
}
// 定时任务管理功能
$cron_message = '';
$cron_jobs = [];
// 定时任务预设模板
$cron_presets = [
    'every_minute' => ['label' => '每分钟', 'expression' => '* * * * *'],
    'every_5_minutes' => ['label' => '每5分钟', 'expression' => '*/5 * * * *'],
    'every_10_minutes' => ['label' => '每10分钟', 'expression' => '*/10 * * * *'],
    'every_30_minutes' => ['label' => '每30分钟', 'expression' => '*/30 * * * *'],
    'every_hour' => ['label' => '每小时', 'expression' => '0 * * * *'],
    'every_2_hours' => ['label' => '每2小时', 'expression' => '0 */2 * * *'],
    'every_6_hours' => ['label' => '每6小时', 'expression' => '0 */6 * * *'],
    'daily_midnight' => ['label' => '每天午夜', 'expression' => '0 0 * * *'],
    'daily_morning' => ['label' => '每天早上6点', 'expression' => '0 6 * * *'],
    'daily_evening' => ['label' => '每天晚上8点', 'expression' => '0 20 * * *'],
    'weekly' => ['label' => '每周日午夜', 'expression' => '0 0 * * 0'],
    'monthly' => ['label' => '每月1日午夜', 'expression' => '0 0 1 * *'],
];
// 获取当前定时任务列表
function get_cron_jobs() {
    exec('crontab -l 2>&1', $output, $return_var);
    if ($return_var !== 0) return [];
    $jobs = [];
    foreach ($output as $line) {
        $line = trim($line);
        if (!empty($line) && strpos($line, '#') !== 0) { // 过滤注释和空行
            $jobs[] = $line;
        }
    }
    return $jobs;
}
// 计算cron表达式的下次执行时间
function get_next_run_time($cron_expr) {
    // 解析cron表达式
    $parts = explode(' ', $cron_expr);
    if (count($parts) != 5) return '无效表达式';
    list($minutes, $hours, $days, $months, $weeks) = $parts;
    // 使用cron-expression库计算下次执行时间
    // 注意: 实际环境中需要安装mtdowling/cron-expression库
    // composer require mtdowling/cron-expression
    try {
        if (class_exists('\Cron\CronExpression')) {
            $cron = \Cron\CronExpression::factory("$minutes $hours $days $months $weeks");
            return $cron->getNextRunDate()->format('Y-m-d H:i:s');
        } else {
            return '需要安装cron-expression库';
        }
    } catch (Exception $e) {
        return '计算失败';
    }
}
// 检查任务是否正在运行
function is_job_running($command) {
    // 简单检查进程是否存在
    $escaped_cmd = escapeshellarg('%' . $command . '%');
    exec("ps aux | grep $escaped_cmd | grep -v grep", $output);
    return count($output) > 0;
}
// 添加定时任务
if (isset($_POST['add_cron'])) {
    $schedule = trim($_POST['cron_schedule']);
    $command = trim($_POST['command']);
    if (empty($schedule) || empty($command)) {
        $cron_message = "错误: 定时规则和命令不能为空";
    } else {
        // 验证cron表达式格式(简易验证)
        if (!preg_match('/^(\*|(\d+|\*\/\d+)) (\*|(\d+|\*\/\d+)) (\*|(\d+|\*\/\d+)) (\*|(\d+|\*\/\d+)) (\*|(\d+|\*\/\d+))$/', $schedule)) {
            $cron_message = "错误: 定时规则格式无效(分 时 日 月 周)";
        } else {
            // 备份当前crontab
            $current_cron = implode("\n", get_cron_jobs()) . "\n";
            // 添加新任务(带注释标识为该工具添加)
            $new_cron = $current_cron . "# Added by ShellExecutor\n" . $schedule . " " . $command . "\n";
            // 写入crontab
            $tmp_file = tempnam(sys_get_temp_dir(), 'cron');
            file_put_contents($tmp_file, $new_cron);
            exec("crontab " . $tmp_file, $output, $return_var);
            unlink($tmp_file);
            if ($return_var === 0) {
                $cron_message = "定时任务添加成功";
            } else {
                $cron_message = "错误: 无法添加定时任务(返回码: $return_var)";
            }
        }
    }
}
// 删除定时任务
if (isset($_GET['delete_cron'])) {
    $cron_index = intval($_GET['delete_cron']);
    $current_jobs = get_cron_jobs();
    if (isset($current_jobs[$cron_index])) {
        // 排除要删除的任务
        $new_jobs = [];
        foreach ($current_jobs as $i => $job) {
            if ($i !== $cron_index) $new_jobs[] = $job;
        }
        // 写入新的crontab
        $tmp_file = tempnam(sys_get_temp_dir(), 'cron');
        file_put_contents($tmp_file, implode("\n", $new_jobs) . "\n");
        exec("crontab " . $tmp_file, $output, $return_var);
        unlink($tmp_file);
        if ($return_var === 0) {
            $cron_message = "定时任务删除成功";
        } else {
            $cron_message = "错误: 无法删除定时任务(返回码: $return_var)";
        }
    } else {
        $cron_message = "错误: 定时任务不存在";
    }
}
// 获取当前定时任务
$cron_jobs = get_cron_jobs();
// 执行命令
$result = ['output' => '', 'success' => false, 'execution_time' => 0];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['command']) && !isset($_POST['save_script']) && !isset($_POST['add_cron'])) {
    $command = trim($_POST['command']);
    // 完全取消所有命令限制,直接执行用户输入的命令
    $start_time = microtime(true);
    exec($command . ' 2>&1', $output_lines, $return_var);
    $end_time = microtime(true);
    $result['execution_time'] = round($end_time - $start_time, 3);
    $result['output'] = implode("\n", $output_lines);
    $result['success'] = ($return_var === 0);
    // 限制输出大小
    if (strlen($result['output']) > $config['max_output_size']) {
        $result['output'] = substr($result['output'], 0, $config['max_output_size']) . "\n\n... 输出内容过大,已截断";
    }
    log_action($command, $result['output'], $result['success']);
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>Shell执行工具</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; }
        .header { background-color: #333; color: white; padding: 15px; border-radius: 5px 5px 0 0; }
        .content { background-color: white; padding: 20px; border-radius: 0 0 5px 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
        .command-box { margin-bottom: 20px; }
        textarea { width: 100%; height: 150px; padding: 10px; margin-bottom: 10px; box-sizing: border-box; border: 1px solid #ddd; border-radius: 4px; font-family: monospace; }
        .button-group { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
        button, input[type="submit"] { padding: 10px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
        button:hover, input[type="submit"]:hover { background-color: #45a049; }
        .btn-danger { background-color: #f44336; }
        .btn-danger:hover { background-color: #d32f2f; }
        .btn-primary { background-color: #2196F3; }
        .btn-primary:hover { background-color: #0b7dda; }
        .btn-secondary { background-color: #607D8B; }
        .btn-secondary:hover { background-color: #4b636e; }
        .btn-cron { background-color: #FF9800; }
        .btn-cron:hover { background-color: #F57C00; }
        .result-box { margin-top: 20px; }
        .result-header { font-weight: bold; margin-bottom: 10px; }
        .result-output { background-color: #f9f9f9; border: 1px solid #ddd; padding: 10px; min-height: 100px; max-height: 400px; overflow: auto; font-family: monospace; white-space: pre-wrap; }
        .status { margin-top: 10px; padding: 5px; border-radius: 3px; }
        .success { background-color: #d4edda; color: #155724; }
        .error { background-color: #f8d7da; color: #721c24; }
        .footer { margin-top: 20px; text-align: center; color: #666; font-size: 0.9em; }
        .history { margin-top: 20px; }
        .history table { width: 100%; border-collapse: collapse; }
        .history th, .history td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        .history th { background-color: #f2f2f2; }
        .scripts { margin-top: 20px; }
        .scripts table { width: 100%; border-collapse: collapse; }
        .scripts th, .scripts td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        .scripts th { background-color: #f2f2f2; }
        .script-form { margin-top: 10px; }
        .script-form input[type="text"] { padding: 8px; border: 1px solid #ddd; border-radius: 4px; width: 200px; margin-right: 10px; }
        .message { padding: 10px; margin-bottom: 10px; border-radius: 4px; }
        .message-success { background-color: #d4edda; color: #155724; }
        .message-error { background-color: #f8d7da; color: #721c24; }
        .upload-form { margin-top: 10px; }
        .upload-form input[type="file"] { margin-right: 10px; }
        .cron-form { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 10px; }
        .cron-form input[type="text"] { padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
        .cron-examples { margin-top: 5px; font-size: 0.9em; color: #666; }
        .cron-table { margin-top: 10px; width: 100%; border-collapse: collapse; }
        .cron-table th, .cron-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        .cron-table th { background-color: #f2f2f2; }
        .preset-buttons { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 5px; }
        .preset-btn { padding: 5px 10px; background-color: #e0e0e0; border: none; border-radius: 4px; cursor: pointer; }
        .preset-btn:hover { background-color: #d0d0d0; }
        .status-badge {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 0.9em;
            font-weight: bold;
        }
        .status-active {
            background-color: #d4edda;
            color: #155724;
        }
        .status-inactive {
            background-color: #f8d7da;
            color: #721c24;
        }
        .status-pending {
            background-color: #fff3cd;
            color: #856404;
        }
        /* 深色主题样式 */
        .dark-theme {
            background-color: #1e1e1e;
            color: #e0e0e0;
        }
        .dark-theme .content {
            background-color: #2d2d2d;
            color: #e0e0e0;
        }
        .dark-theme .header {
            background-color: #1a1a1a;
        }
        .dark-theme textarea,
        .dark-theme input,
        .dark-theme select {
            background-color: #3a3a3a;
            color: #e0e0e0;
            border-color: #555;
        }
        .dark-theme .result-output {
            background-color: #3a3a3a;
            color: #e0e0e0;
            border-color: #555;
        }
        .dark-theme .scripts table,
        .dark-theme .history table,
        .dark-theme .cron-table {
            color: #e0e0e0;
        }
        .dark-theme .scripts th,
        .dark-theme .history th,
        .dark-theme .cron-table th {
            background-color: #3a3a3a;
        }
        .dark-theme .scripts td,
        .dark-theme .history td,
        .dark-theme .cron-table td {
            border-color: #555;
        }
        .dark-theme .preset-btn {
            background-color: #4a4a4a;
            color: #e0e0e0;
        }
        .dark-theme .preset-btn:hover {
            background-color: #5a5a5a;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Shell执行工具</h1>
        </div>
        <div class="content">
            <?php if (!empty($script_message)): ?>
            <div class="message <?php echo strpos($script_message, '错误') !== false ? 'message-error' : 'message-success'; ?>">
                <?php echo htmlspecialchars($script_message); ?>
            </div>
            <?php endif; ?>
            <?php if (!empty($cron_message)): ?>
            <div class="message <?php echo strpos($cron_message, '错误') !== false ? 'message-error' : 'message-success'; ?>">
                <?php echo htmlspecialchars($cron_message); ?>
            </div>
            <?php endif; ?>
            <div class="button-group">
                <button id="clear-btn">清空命令</button>
                <button id="select-all-btn">全选命令</button>
                <button id="toggle-theme-btn">切换主题</button>
            </div>
            <div class="command-box">
                <form method="post" enctype="multipart/form-data">
                    <textarea name="command" placeholder="输入要执行的Shell命令..."><?php echo isset($_POST['command']) ? htmlspecialchars($_POST['command']) : ''; ?></textarea>
                    <div class="button-group">
                        <input type="submit" value="执行命令">
                        <div class="script-form">
                            <input type="text" name="script_name" placeholder="脚本名称">
                            <input type="submit" name="save_script" value="保存为脚本" class="btn-primary">
                        </div>
                        <div class="upload-form">
                            <input type="file" name="upload_script" accept=".sh">
                            <input type="submit" name="upload" value="导入脚本" class="btn-secondary">
                        </div>
                    </div>
                    <!-- 改进的定时任务表单 -->
                    <div class="cron-form">
                        <select id="cron_preset" style="padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
                            <option value="">-- 选择预设定时规则 --</option>
                            <?php foreach ($cron_presets as $key => $preset): ?>
                                <option value="<?php echo htmlspecialchars($preset['expression']); ?>"><?php echo htmlspecialchars($preset['label']); ?></option>
                            <?php endforeach; ?>
                        </select>
                        <input type="text" name="cron_schedule" id="cron_schedule" placeholder="定时规则 (分 时 日 月 周)" style="flex: 1;">
                        <input type="submit" name="add_cron" value="添加定时任务" class="btn-cron">
                    </div>
                    <div class="preset-buttons">
                        <strong>常用预设:</strong>
                        <?php foreach (array_slice($cron_presets, 0, 6) as $key => $preset): ?>
                            <button type="button" class="preset-btn" data-cron="<?php echo htmlspecialchars($preset['expression']); ?>"><?php echo htmlspecialchars($preset['label']); ?></button>
                        <?php endforeach; ?>
                    </div>
                    <div class="cron-examples">
                        <p>或者手动输入定时规则(高级用户):</p>
                        <ul>
                            <li><code>* * * * *</code> - 每分钟执行一次</li>
                            <li><code>0 * * * *</code> - 每小时执行一次</li>
                            <li><code>0 0 * * *</code> - 每天凌晨执行一次</li>
                        </ul>
                    </div>
                </form>
            </div>
            <?php if (isset($result['output'])): ?>
            <div class="result-box">
                <div class="result-header">
                    执行结果 (耗时: <?php echo $result['execution_time']; ?> 秒)
                </div>
                <div class="result-output">
                    <?php echo htmlspecialchars($result['output']); ?>
                </div>
                <div class="status <?php echo $result['success'] ? 'success' : 'error'; ?>">
                    <?php echo $result['success'] ? '命令执行成功 (返回码: 0)' : '命令执行失败 (返回码: ' . (isset($return_var) ? $return_var : '未知') . ')'; ?>
                </div>
            </div>
            <?php endif; ?>
            <!-- 改进的定时任务列表 -->
            <div class="scripts">
                <h3>定时任务列表</h3>
                <?php if (!empty($cron_jobs)): ?>
                <table class="cron-table">
                    <tr>
                        <th>序号</th>
                        <th>定时规则</th>
                        <th>命令</th>
                        <th>下次执行时间</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                    <?php foreach ($cron_jobs as $index => $job): ?>
                    <tr>
                        <td><?php echo $index + 1; ?></td>
                        <td><?php echo htmlspecialchars(implode(' ', array_slice(explode(' ', $job), 0, 5))); ?></td>
                        <td><?php echo htmlspecialchars(implode(' ', array_slice(explode(' ', $job), 5))); ?></td>
                        <td>
                            <?php
                            $schedule = implode(' ', array_slice(explode(' ', $job), 0, 5));
                            echo get_next_run_time($schedule);
                            ?>
                        </td>
                        <td>
                            <?php
                            $command = implode(' ', array_slice(explode(' ', $job), 5));
                            $is_running = is_job_running($command);
                            $next_run = get_next_run_time($schedule);
                            if ($next_run === '无效表达式' || $next_run === '计算失败' || $next_run === '需要安装cron-expression库') {
                                echo '<span class="status-badge status-inactive">无效</span>';
                            } elseif ($is_running) {
                                echo '<span class="status-badge status-active">运行中</span>';
                            } else {
                                echo '<span class="status-badge status-pending">待执行</span>';
                            }
                            ?>
                        </td>
                        <td>
                            <a href="?delete_cron=<?php echo $index; ?>" class="btn-danger" onclick="return confirm('确定要删除这个定时任务吗?')">删除</a>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </table>
                <?php else: ?>
                <p>暂无定时任务</p>
                <?php endif; ?>
            </div>
            <div class="scripts">
                <h3>已保存的脚本</h3>
                <?php if (!empty($scripts)): ?>
                <table>
                    <tr>
                        <th>名称</th>
                        <th>大小</th>
                        <th>修改时间</th>
                        <th>操作</th>
                    </tr>
                    <?php foreach ($scripts as $script): ?>
                    <tr>
                        <td><?php echo htmlspecialchars($script['name']); ?></td>
                        <td><?php echo round($script['size'] / 1024, 2); ?> KB</td>
                        <td><?php echo date('Y-m-d H:i:s', $script['mtime']); ?></td>
                        <td>
                            <a href="?load_script=<?php echo urlencode($script['path']); ?>" class="btn-primary">加载</a>
                            <a href="?delete_script=<?php echo urlencode($script['path']); ?>" class="btn-danger" onclick="return confirm('确定要删除这个脚本吗?')">删除</a>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </table>
                <?php else: ?>
                <p>暂无保存的脚本</p>
                <?php endif; ?>
            </div>
            <div class="history">
                <h3>最近执行历史</h3>
                <?php
                if (file_exists($config['log_file'])) {
                    $log_lines = array_reverse(file($config['log_file'], FILE_IGNORE_NEW_LINES));
                    $history = [];
                    foreach ($log_lines as $line) {
                        if (preg_match('/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (.*?) - (.*?) - (SUCCESS|ERROR) - (.*)$/', $line, $matches)) {
                            $history[] = [
                                'time' => $matches[1],
                                'ip' => $matches[2],
                                'user' => $matches[3],
                                'status' => $matches[4],
                                'command' => $matches[5]
                            ];
                            if (count($history) >= 5) break;
                        }
                    }
                    if (!empty($history)) {
                        echo '<table>';
                        echo '<tr><th>时间</th><th>IP地址</th><th>用户</th><th>状态</th><th>命令</th></tr>';
                        foreach ($history as $entry) {
                            $status_class = $entry['status'] === 'SUCCESS' ? 'success' : 'error';
                            echo '<tr>';
                            echo '<td>' . htmlspecialchars($entry['time']) . '</td>';
                            echo '<td>' . htmlspecialchars($entry['ip']) . '</td>';
                            echo '<td>' . htmlspecialchars($entry['user']) . '</td>';
                            echo '<td class="' . $status_class . '">' . htmlspecialchars($entry['status']) . '</td>';
                            echo '<td>' . htmlspecialchars($entry['command']) . '</td>';
                            echo '</tr>';
                        }
                        echo '</table>';
                    } else {
                        echo '<p>暂无执行历史</p>';
                    }
                } else {
                    echo '<p>日志文件不存在或无法访问</p>';
                }
                ?>
            </div>
        </div>
        <div class="footer">
            <p><strong>警告:此工具已完全取消所有命令限制,存在致命安全风险!</strong></p>
            <p>请勿在任何与公共网络连接的服务器上使用此工具,可能导致服务器被入侵和数据泄露!</p>
            <?php if ($config['require_auth']): ?>
            <p><a href="?logout=1">退出登录</a></p>
            <?php endif; ?>
        </div>
    </div>
    <script>
        // 清空命令文本框
        document.getElementById('clear-btn').addEventListener('click', function() {
            document.querySelector('textarea[name="command"]').value = '';
        });
        // 全选命令文本框内容
        document.getElementById('select-all-btn').addEventListener('click', function() {
            const textarea = document.querySelector('textarea[name="command"]');
            textarea.select();
        });
        // 切换深色/浅色主题
        document.getElementById('toggle-theme-btn').addEventListener('click', function() {
            document.body.classList.toggle('dark-theme');
            // 保存主题偏好到localStorage
            const isDarkTheme = document.body.classList.contains('dark-theme');
            localStorage.setItem('shellExecutorTheme', isDarkTheme ? 'dark' : 'light');
        });
        // 加载保存的主题偏好
        document.addEventListener('DOMContentLoaded', function() {
            const savedTheme = localStorage.getItem('shellExecutorTheme');
            if (savedTheme === 'dark') {
                document.body.classList.add('dark-theme');
            }
        });
        // 预设按钮点击事件
        document.querySelectorAll('.preset-btn').forEach(btn => {
            btn.addEventListener('click', function() {
                const cronExpr = this.getAttribute('data-cron');
                document.getElementById('cron_schedule').value = cronExpr;
            });
        });
        // 预设选择器变更事件
        document.getElementById('cron_preset').addEventListener('change', function() {
            const selectedValue = this.value;
            if (selectedValue) {
                document.getElementById('cron_schedule').value = selectedValue;
            }
        });
        // 文件上传处理
        document.querySelector('input[name="upload_script"]').addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    document.querySelector('textarea[name="command"]').value = e.target.result;
                };
                reader.readAsText(file);
            }
        });
    </script>
</body>
</html>

下载

下载地址:

给飞牛添加一个web的shell执行工具 root权限-作死版(图5)Shell 命令执行工具.rar

收藏
送赞
分享

0

主题

1

回帖

0

牛值

江湖小虾

2025-7-1 15:33:15 显示全部楼层
还有1panel也可以

2

主题

2

回帖

0

牛值

江湖小虾

2025-7-2 18:00:46 楼主 显示全部楼层

飞牛软件的第三方的面板兼容不好1panel和宝塔软件商城我都装过,会各种报错,也不是最新版本的。这个是我在搭建php环境网站的顺便稿的一个东西。目前用的小皮的面板没有什么报错问题。

2

主题

2

回帖

0

牛值

江湖小虾

2025-7-2 18:02:30 楼主 显示全部楼层
congyong 发表于 2025-7-1 20:59
直接用 自带的 浏览器, vnc 里面有个 Xterminal, 在里面 ssh 到宿主机----   零成本,内网穿透都解决了
...

主要是界面化可用直接访问比较方便点,那个docker游览器有时候不是很好用而且也麻烦

1

主题

57

回帖

0

牛值

初出茅庐

2025-7-4 09:01:10 显示全部楼层

这工具不错

1

主题

18

回帖

0

牛值

江湖小虾

2025-7-4 10:34:32 显示全部楼层
congyong 发表于 2025-7-1 20:59
直接用 自带的 浏览器, vnc 里面有个 Xterminal, 在里面 ssh 到宿主机----   零成本,内网穿透都解决了
...

自带的浏览器哪儿有Xterminal啊?
右上角最小化,或者直接右上角关闭,会进入黑屏,黑屏右键就有xterm和Chromium两种选择  详情 回复
6 天前

10

主题

54

回帖

0

牛值

初出茅庐

哇塞,确实不错,适用于不需要三方面板管理又需要web控制台的情况

飞牛魔修

1

主题

4

回帖

0

牛值

江湖小虾

Tsukisan 发表于 2025-7-4 10:34
自带的浏览器哪儿有Xterminal啊?

右上角最小化,或者直接右上角关闭,会进入黑屏,黑屏右键就有xterm和Chromium两种选择
我试了一下好像无法显示中文,不支持zh_CN.UTF8吗?这个有没有办法解决呢?  详情 回复
5 天前
可以,又学到一招哈哈,之前我就在想不小心点到最小化或者关闭之后黑屏无法操作,只能重启docker应用有点麻烦,原来是自己没玩明白  详情 回复
5 天前

1

主题

18

回帖

0

牛值

江湖小虾

-木南- 发表于 2025-7-8 15:46
右上角最小化,或者直接右上角关闭,会进入黑屏,黑屏右键就有xterm和Chromium两种选择 ...

可以,又学到一招哈哈,之前我就在想不小心点到最小化或者关闭之后黑屏无法操作,只能重启docker应用有点麻烦,原来是自己没玩明白

1

主题

18

回帖

0

牛值

江湖小虾

-木南- 发表于 2025-7-8 15:46
右上角最小化,或者直接右上角关闭,会进入黑屏,黑屏右键就有xterm和Chromium两种选择 ...

我试了一下好像无法显示中文,不支持zh_CN.UTF8吗?这个有没有办法解决呢?
这个我也不是很清楚了,我也不算专业  详情 回复
3 天前

1

主题

4

回帖

0

牛值

江湖小虾

Tsukisan 发表于 2025-7-9 10:38
我试了一下好像无法显示中文,不支持zh_CN.UTF8吗?这个有没有办法解决呢? ...

这个我也不是很清楚了,我也不算专业
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则