当前位置: 首页 > news >正文

CTF-WEB排行榜制作

CTF-WEB排行榜制作

项目需求:
现在14道题对应有14个flag,我需要使用dockerfile搭建一个简单的,能够实现验证这些题目对应的flag来计分的简单网站(要求页面比较精美) 
前十题设置为10分
11-14题设置为20分
1.	flag{5a3dR7vKpQ9wXyZ2}
2.	flag{8G4hLbNqFcTjMnP6}
3.	flag{3V9sEkYmWuHxDrz1}
4.	flag{J7r2tLqZfNpD4mKs}
5.	flag{B6nQwRvXcT3yUh9P}
6.	flag{qW4eF8gHjK2lO7iU}
7.	flag{M9aZsEdCvFbRtY3N}
8.	flag{2Lp5oIuG7yTqW1rD}
9.	flag{R4vNkSmXzHcJ8wB3}
10.	flag{6dFgHjKl9pOiU7yT}
11.	flag{W3zYcT8rDxV5sQmK}
12.	flag{9PnZtLwEfR6vGhJ4}
13.	flag{7UkXbMqAsHdF2iN1}
14.	flag{yT4jKmP9oLvRgS8e}

目录结构打印(项目根目录为ctf):

ctf/
├── docker-compose.yml
├── init.sql
└── web/
    ├── Dockerfile
    ├── flags.php
    ├── index.php
    ├── leaderboard.php
    ├── submit.php
    ├── style.css
    └── db.php

创建目录结构命令:

# 创建目录结构与文件
# 切换到工作目录(例如 /root 或 ~/)
mkdir -p ~/ctf && cd ~/ctf

# 创建顶层目录和文件
mkdir -p ctf/web
touch ctf/docker-compose.yml
touch ctf/init.sql

# 创建 web 子目录下的文件
touch ctf/web/Dockerfile
touch ctf/web/flags.php
touch ctf/web/index.php
touch ctf/web/leaderboard.php
touch ctf/web/submit.php
touch ctf/web/style.css
touch ctf/web/db.php
tree ctf

flags.php:题目flag和分值配置(包含前五奖励逻辑支持)

init.sql:MySQL建表与初始化

submit.php:处理flag提交与写入逻辑

leaderboard.php:统计并展示排行榜

index.php:主页面

Dockerfile + docker-compose.yml:构建环境配置

ctf/web/flags.php

cat > ctf/web/flags.php <<EOF
<?php
// 已整合到 submit.php 逻辑中
// 此文件保留用于后期扩展
header("Location: index.php");
exit();
?>
EOF

ctf/init.sql

cat > ctf/init.sql<<EOF
CREATE TABLE IF NOT EXISTS challenges (
    id INT PRIMARY KEY AUTO_INCREMENT,
    flag VARCHAR(50) UNIQUE NOT NULL,
    score INT NOT NULL,
    solved_count INT DEFAULT 0,
    first_five JSON DEFAULT NULL
);

CREATE TABLE IF NOT EXISTS users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    total_score INT DEFAULT 0
);

CREATE TABLE IF NOT EXISTS submissions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    challenge_id INT NOT NULL,
    submit_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    is_correct BOOLEAN,
    is_in_top5 BOOLEAN DEFAULT FALSE,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (challenge_id) REFERENCES challenges(id)
);

-- 初始化题目数据
INSERT INTO challenges (flag, score) VALUES
('flag{5a3dR7vKpQ9wXyZ2}', 10),
('flag{8G4hLbNqFcTjMnP6}', 10),
('flag{3V9sEkYmWuHxDrz1}', 10),
('flag{J7r2tLqZfNpD4mKs}', 10),
('flag{B6nQwRvXcT3yUh9P}', 10),
('flag{qW4eF8gHjK2lO7iU}', 10),
('flag{M9aZsEdCvFbRtY3N}', 10),
('flag{2Lp5oIuG7yTqW1rD}', 10),
('flag{R4vNkSmXzHcJ8wB3}', 10),
('flag{6dFgHjKl9pOiU7yT}', 10),
('flag{W3zYcT8rDxV5sQmK}', 20),
('flag{9PnZtLwEfR6vGhJ4}', 20),
('flag{7UkXbMqAsHdF2iN1}', 20),
('flag{yT4jKmP9oLvRgS8e}', 20);
EOF

ctf/docker-compose.yml

cat > ctf/docker-compose.yml <<EOF
version: '3.8'

services:
  web:
    build: ./web
    ports:
      - "80:80"
    depends_on:
      - mysql
    environment:
      MYSQL_HOST: mysql
    networks:
      - ctf-net

  mysql:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: ctf123
      MYSQL_DATABASE: ctf_db
      MYSQL_USER: ctf_user
      MYSQL_PASSWORD: ctf_pass
    volumes:
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - mysql-data:/var/lib/mysql
    networks:
      - ctf-net
    restart: always

volumes:
  mysql-data:

networks:
  ctf-net:
EOF

ctf/web/Dockerfile

cat > ctf/web/Dockerfile <<'EOF'
FROM php:7.4-apache

# 使用阿里云镜像源
RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list

# 安装扩展并配置
RUN apt-get update && \
    apt-get install -y libzip-dev libpng-dev && \
    docker-php-ext-install pdo_mysql zip gd && \
    a2enmod rewrite

# 配置PHP
RUN echo "upload_max_filesize = 10M" >> /usr/local/etc/php/php.ini && \
    echo "post_max_size = 10M" >> /usr/local/etc/php/php.ini

COPY . /var/www/html/
EOF

ctf/web/submit.php

cat > ctf/web/submit.php <<'EOF'
<?php
require 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $flag = trim($_POST['flag']);
    
    // 验证输入
    if (empty($username) || empty($flag)) {
        die("用户名和Flag不能为空");
    }

    try {
        $pdo->beginTransaction();

        // 获取用户或创建用户
        $stmt = $pdo->prepare("INSERT IGNORE INTO users (username) VALUES (?)");
        $stmt->execute([$username]);
        $user_id = $pdo->lastInsertId();
        if ($user_id == 0) {
            $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
            $stmt->execute([$username]);
            $user_id = $stmt->fetchColumn();
        }

        // 验证Flag
        $stmt = $pdo->prepare("SELECT id, score FROM challenges WHERE flag = ?");
        $stmt->execute([$flag]);
        $challenge = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($challenge) {
            // 检查是否重复提交
            $stmt = $pdo->prepare("SELECT id FROM submissions 
                                WHERE user_id = ? AND challenge_id = ?");
            $stmt->execute([$user_id, $challenge['id']]);
            if ($stmt->fetch()) {
                die("请勿重复提交相同Flag");
            }

            // 更新题目解决数
            $pdo->prepare("UPDATE challenges SET solved_count = solved_count + 1 WHERE id = ?")
                ->execute([$challenge['id']]);

            // 计算奖励分
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM submissions 
                                WHERE challenge_id = ? AND is_correct = 1");
            $stmt->execute([$challenge['id']]);
            $current_solved = $stmt->fetchColumn();

            $bonus = 0;
            if ($current_solved < 5) {
                $bonus = 5 - $current_solved;
            }

            // 更新用户积分
            $total_score = $challenge['score'] + $bonus;
            $pdo->prepare("UPDATE users SET total_score = total_score + ? WHERE id = ?")
                ->execute([$total_score, $user_id]);

            // 记录提交
            $stmt = $pdo->prepare("INSERT INTO submissions 
                                (user_id, challenge_id, is_correct, is_in_top5)
                                VALUES (?, ?, 1, ?)");
            $stmt->execute([$user_id, $challenge['id'], ($bonus > 0 ? 1 : 0)]);

            echo "提交成功!获得积分:$total_score";
        } else {
            // 记录错误提交
            $pdo->prepare("INSERT INTO submissions (user_id, is_correct) VALUES (?, 0)")
                ->execute([$user_id]);
            echo "Flag错误!";
        }

        $pdo->commit();
    } catch (Exception $e) {
        $pdo->rollBack();
        die("系统错误:" . $e->getMessage());
    }
}
?>
EOF

ctf/web/leaderboard.php

cat > ctf/web/leaderboard.php <<'EOF'
<?php require 'db.php'; ?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>排行榜</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>实时排行榜</h1>
        <table class="leaderboard">
            <thead>
                <tr>
                    <th>排名</th>
                    <th>用户名</th>
                    <th>总分</th>
                    <th>最后提交</th>
                </tr>
            </thead>
            <tbody>
                <?php
                $stmt = $pdo->query("
                    SELECT u.username, u.total_score, MAX(s.submit_time) as last_submit
                    FROM users u
                    LEFT JOIN submissions s ON u.id = s.user_id
                    GROUP BY u.id
                    ORDER BY u.total_score DESC, last_submit ASC
                    LIMIT 50
                ");
                $rank = 1;
                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                    echo "<tr>";
                    echo "<td>#$rank</td>";
                    echo "<td>{$row['username']}</td>";
                    echo "<td>{$row['total_score']}</td>";
                    echo "<td>" . date('Y-m-d H:i', strtotime($row['last_submit'])) . "</td>";
                    echo "</tr>";
                    $rank++;
                }
                ?>
            </tbody>
        </table>
        <div class="back-link">
            <a href="index.php">← 返回挑战列表</a>
        </div>
    </div>
</body>
</html>
EOF

ctf/web/index.php

cat > ctf/web/index.php <<'EOF'
<?php require 'db.php'; ?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>CTF挑战平台</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>CTF挑战列表</h1>
        <div class="challenge-list">
            <?php
            $stmt = $pdo->query("SELECT * FROM challenges");
            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                $points = $row['score'];
                echo "<div class='challenge'>";
                echo "<span class='challenge-id'>题目 {$row['id']}</span>";
                echo "<span class='points'>分值:{$points}</span>";
                echo "<span class='solved'>已解出:{$row['solved_count']}次</span>";
                echo "</div>";
            }
            ?>
        </div>
        
        <div class="submit-form">
            <h2>提交Flag</h2>
            <form action="submit.php" method="POST">
                <input type="text" name="username" placeholder="用户名" required>
                <input type="text" name="flag" placeholder="输入Flag" required>
                <button type="submit">提交</button>
            </form>
        </div>
        
        <div class="leaderboard-link">
            <a href="leaderboard.php">查看实时排行榜 →</a>
        </div>
    </div>
</body>
</html>
EOF

CTF/web/db.php

cat > ctf/web/db.php <<EOF
<?php
$host = 'mysql';
$dbname = 'ctf_db';
$user = 'ctf_user';
$pass = 'ctf_pass';

try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    die("Database connection failed: " . $e->getMessage());
}
?>
EOF

CTF/web/style.css

cat > ctf/web/style.css <<EOF
/* 基础样式 */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    margin: 0;
    padding: 20px;
    background-color: #f5f7fa;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    background: white;
    padding: 30px;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

/* 挑战列表样式 */
.challenge-list {
    margin-bottom: 40px;
}

.challenge {
    background: #f8f9fc;
    padding: 15px 20px;
    margin: 10px 0;
    border-radius: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: transform 0.2s;
}

.challenge:hover {
    transform: translateX(10px);
}

.challenge-id {
    font-weight: 600;
    color: #2d3748;
}

.points {
    color: #48bb78;
    font-weight: bold;
}

.solved {
    color: #718096;
}

/* 提交表单样式 */
.submit-form {
    background: #f8f9fc;
    padding: 25px;
    border-radius: 8px;
    margin-top: 30px;
}

.submit-form input[type="text"] {
    width: 250px;
    padding: 12px;
    margin-right: 10px;
    border: 2px solid #e2e8f0;
    border-radius: 6px;
    font-size: 16px;
}

.submit-form button {
    background: #4299e1;
    color: white;
    border: none;
    padding: 12px 25px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px;
    transition: background 0.3s;
}

.submit-form button:hover {
    background: #3182ce;
}

/* 排行榜样式 */
.leaderboard {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}

.leaderboard th, .leaderboard td {
    padding: 15px;
    text-align: left;
    border-bottom: 1px solid #e2e8f0;
}

.leaderboard th {
    background: #4299e1;
    color: white;
}

.leaderboard tr:nth-child(even) {
    background: #f8fafc;
}

.back-link {
    margin-top: 25px;
    text-align: center;
}

.back-link a {
    color: #4299e1;
    text-decoration: none;
    font-weight: 500;
}

.leaderboard-link {
    text-align: center;
    margin-top: 25px;
}

.leaderboard-link a {
    color: #48bb78;
    text-decoration: none;
    font-weight: 600;
    font-size: 1.1em;
}
EOF

构建和运行命令:

cd ctf
docker-compose build
docker-compose up -d

访问测试:
浏览器打开 http://localhost

常见问题解决:

  1. 如果遇到权限问题,在命令前加sudo
  2. 如果端口冲突,修改docker-compose.yml中的端口映射为 “8080:80”
  3. 清除环境使用:docker-compose down -v
  4. 查看日志:docker-compose logs -f

以上命令会:

  1. 创建完整的目录结构
  2. 生成所有配置文件
  3. 构建PHP+Apache镜像
  4. 启动MySQL数据库
  5. 自动初始化数据库结构
  6. 启动网站服务
  7. 自动设置好题目flag和评分规则

相关文章:

  • JavaWeb 课堂笔记 —— 10 MySQL DML + DQL
  • Node.js介绍
  • vue入门:函数式组件
  • 实现一个动态验证码生成器:Canvas与JavaScript的完美结合
  • 《C语言中的形参与实参:理解函数调用的核心概念》
  • NVIDIA AI Aerial
  • docker 安装 jenkins
  • SpringBoot实战2
  • 【强化学习-蘑菇书-3】马尔可夫性质,马尔可夫链,马尔可夫过程,马尔可夫奖励过程,如何计算马尔可夫奖励过程里面的价值
  • 奇怪的电梯——DFS算法
  • linux多线(进)程编程——(4)进程间的传音术(命名管道)
  • Android envsetup与Python venv使用指南
  • CST1017.基于Spring Boot+Vue共享单车管理系统
  • 【软考系统架构设计师】软件工程知识点
  • AI agents系列之全面介绍
  • 密码加密方式
  • 【基础算法】递推算法 - java
  • go之为什么学go?
  • 常用AI辅助编程工具及平台介绍
  • 数据集 handpose_x_plus 3D RGB 三维手势 - 手工绘画 场景 draw picture
  • 江苏疾控:下设部门无“病毒研究所”,常荣山非本单位工作人员
  • 特写|银耳种植“北移”到沧州盐山,村民入伙可年增收4万元
  • 第十一届世界雷达展开幕,尖端装备、“大国重器”集中亮相
  • 蔡建忠已任昆山市副市长、市公安局局长
  • 侵害孩子者,必严惩不贷!3名性侵害未成年人罪犯被执行死刑
  • 北方产粮大省遭遇气象干旱,夏粮用水如何解决?