1. 从零开始:CTFd与Web靶场部署基础

搞CTF的朋友都知道,Web题目是最让人又爱又恨的类型。爱的是它能模拟真实漏洞场景,恨的是部署起来总是一堆坑。去年我帮学校搭CTF比赛平台时,光是部署Web题目就折腾了整整两天。后来发现了CTFTraining这个宝藏题库和Docker-Compose的组合,部署效率直接提升10倍。

先说下基本配置要求。你需要:

  • 已经搭建好的CTFd平台(建议1.3.0以上版本)
  • Linux服务器(Ubuntu 18.04/20.04最稳定)
  • Docker环境(版本19.03+)
  • 基础命令行操作能力

为什么推荐这个方案?实测下来有三个明显优势:

  1. 题库标准化:CTFTraining收集了历年各大比赛的真题,环境配置都已经调试好
  2. 隔离性强:每个题目独立容器,互不干扰
  3. 快速回滚:题目出问题时,一条命令就能重置

我第一次用这个方案部署CISCN 2019的Web题,从下载到上线只用了7分钟。相比之前手动配环境动不动就报错,简直是降维打击。

2. 环境准备:Docker全家桶配置指南

2.1 Docker引擎安装

很多教程会推荐用curl -sSL https://get.docker.com | sh一键安装,但我强烈建议用apt手动安装。一键脚本经常会把测试版包装上,后面各种兼容性问题。这是我验证过最稳的安装方式:

# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc

# 安装依赖
sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 添加官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 设置稳定版仓库
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装Docker引擎
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

装完后一定要测试:

sudo docker run hello-world

看到"Hello from Docker!"才算成功。如果卡在pull镜像,记得配置国内镜像源:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://你的镜像地址.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

2.2 Docker-Compose安装

虽然现在Docker自带compose插件,但CTFTraining的题目都是按老版本写的,建议还是装独立的docker-compose:

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

验证版本:

docker-compose --version
# 应该显示 1.29.2

这里有个坑:如果服务器在国内,GitHub可能连不上。可以先用本地机器下载,再scp传上去:

scp docker-compose-Linux-x86_64 user@your_server:/usr/local/bin/docker-compose

3. 题库部署实战:以CISCN2019真题为例

3.1 题目获取与目录规划

CTFTraining的题目都在GitHub上,但直接clone整个仓库没必要。我习惯按比赛年份分类存放:

mkdir -p ~/CTF/{CISCN,CTFHub,RealWorld}/web
cd ~/CTF/CISCN/web
git clone https://github.com/CTFTraining/ciscn_2019_web_northern_china_day1_web1.git

目录结构建议这样组织:

~/CTF/
├── CISCN/
│   ├── web/
│   │   ├── ciscn_2019_web1/
│   │   │   ├── docker-compose.yml
│   │   │   ├── www/
│   │   │   └── ...
├── CTFHub/
└── RealWorld/

3.2 配置文件魔改技巧

用vim打开docker-compose.yml后,重点修改三个地方:

version: '2'
services:
  web:
    build: .
    ports:
      - "0.0.0.0:12345:80"  # 修改端口映射
    environment:
      - FLAG=flag{test_flag_123}  # 设置自定义flag
    restart: always

避坑指南

  1. 端口建议选30000-40000范围,避免冲突
  2. flag格式要符合CTFd校验规则(默认要求flag{}包裹)
  3. 如果题目需要MySQL等组件,要检查links配置

我遇到过最奇葩的问题是题目需要特定PHP版本,但dockerfile里没指定。这时候要自己改Dockerfile:

FROM php:5.6-apache  # 明确指定版本
RUN docker-php-ext-install mysql
COPY www/ /var/www/html/

3.3 容器启动与调试

启动命令看似简单,但有些隐藏参数很实用:

docker-compose up -d --build  # 强制重新构建镜像
docker-compose logs -f web    # 实时查看日志

常见问题排查:

  • 端口占用:netstat -tulnp | grep 端口号
  • 容器启动失败:docker inspect 容器ID | grep -A 10 "State"
  • 题目无法访问:检查iptables规则 sudo iptables -L -n

有一次部署某SSRF题目时,容器一直重启。后来发现是题目代码里有exit(0)导致服务退出。解决方法是在docker-compose.yml里加:

    stdin_open: true
    tty: true

4. CTFd集成:题目上线全流程

4.1 前端配置要点

登录CTFd后台,在"Challenges"页面点击"New Challenge",关键配置项:

  • Name:题目名称(建议包含年份和比赛名称)
  • Category:Web
  • Value:动态分值建议初始500
  • Description:可以用HTML写解题提示

最容易被忽略的是Connection Info字段,格式必须是:

http://你的服务器IP:端口号

4.2 Flag设置技巧

在"Flags"标签页添加flag时,有几个实用功能:

  1. 动态flag:可以用{{}}模板变量
  2. 正则匹配:比如设置flag{.*}匹配任意flag
  3. 区分大小写:根据题目需求勾选

实测发现一个坑:如果flag里含特殊字符如$,需要加转义符\

4.3 题目测试流程

上线前一定要走完整测试流程:

  1. 用参赛者账号登录尝试解题
  2. 检查flag提交是否正常
  3. 测试多次错误提交后的提示
  4. 验证容器在高并发下的稳定性

我曾经遇到过选手提交flag后CTFd没反应,最后发现是Nginx配置了body大小限制。解决方法是在CTFd的Nginx配置里加:

client_max_body_size 10M;

5. 高阶技巧与故障处理

5.1 批量部署方案

当需要部署20+题目时,手动操作会累死。可以用这个脚本自动处理:

#!/bin/bash
for dir in ~/CTF/*/web/*/; do
  cd "$dir" || exit
  docker-compose up -d
  port=$(grep -oP 'ports:.*?\K\d+' docker-compose.yml)
  echo "${dir} 已启动,端口 ${port}"
done

更专业的做法是用Ansible,这里给出playbook示例:

- hosts: ctf_servers
  tasks:
    - name: 部署Web题目
      docker_compose:
        project_src: "/home/ctf/{{ item }}"
        state: present
      with_items:
        - "CISCN/web/ciscn_2019_web1"
        - "CTFHub/web/sql_injection"

5.2 资源监控与优化

用这个命令监控容器资源占用:

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

遇到性能瓶颈时可以:

  1. 限制CPU:docker-compose.yml里加cpus: 0.5
  2. 限制内存:mem_limit: 512m
  3. 重启策略:restart: unless-stopped

5.3 常见报错解决方案

问题1:端口冲突

ERROR: for web Cannot start service web: driver failed programming external connectivity on endpoint web (): Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use

解决:

sudo ss -tulnp | grep 80  # 找出占用进程
sudo systemctl stop nginx  # 停止冲突服务

问题2:权限拒绝

Got permission denied while trying to connect to the Docker daemon socket

解决:

sudo usermod -aG docker $USER
newgrp docker

问题3:镜像拉取失败

ERROR: pull access denied for web, repository does not exist or may require 'docker login'

解决:检查docker-compose.yml里的image名称是否拼写错误

6. 安全加固与维护

6.1 容器隔离配置

默认配置下容器有安全隐患,建议在docker-compose.yml里添加:

    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp:rw,size=10M

6.2 日志收集方案

用ELK收集题目日志的配置示例:

    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://你的ELK服务器:514"
        tag: "{{.Name}}"

6.3 定期维护脚本

这个脚本可以清理旧容器和悬空镜像:

#!/bin/bash
# 停止所有运行中的题目
docker stop $(docker ps -aq --filter "label=com.docker.compose.project=ctf")

# 清理一周前的日志
find /var/lib/docker/containers -name "*.log" -mtime +7 -delete

# 移除悬空镜像
docker image prune -f

# 重启所有题目
docker start $(docker ps -aq --filter "label=com.docker.compose.project=ctf")

建议每天凌晨3点运行:

0 3 * * * /path/to/cleanup.sh >> /var/log/ctf_clean.log 2>&1

7. 实战案例:部署一个完整比赛

去年给某高校搭建CTF比赛时,我用这套方案部署了30道Web题。具体步骤:

  1. 选题规划

    • 简单题:5道(SQL注入、XSS等基础漏洞)
    • 中等题:15道(SSRF、反序列化等)
    • 难题:10道(内存破坏、沙箱逃逸等)
  2. 服务器配置

    • 8核16G云服务器 × 3台
    • 每台部署10道题
    • 用Nginx做负载均衡
  3. 部署时间表

    • 环境准备:1小时
    • 批量部署题目:2小时
    • 压力测试:1小时
    • 监控配置:0.5小时

比赛期间遇到最棘手的问题是某道题的容器不断重启。后来发现是选手的爆破请求触发了PHP-FPM的max_children限制。临时解决方案是:

docker exec -it 容器ID bash -c "echo 'pm.max_children = 100' >> /etc/php/7.4/fpm/php-fpm.conf && service php7.4-fpm restart"

这个方案最大的优势是弹性扩展。当监测到某道题访问量激增时,可以快速克隆到新服务器:

# 在新服务器上
rsync -avzP root@原服务器:/home/CTF/CISCN/web/ciscn_2019_web1 .
docker-compose up -d --scale web=3  # 启动3个实例

更多推荐