利用Docker搭建CTF比赛动态靶机
2022-8-24 08:41:14 Author: 0x00实验室(查看原文) 阅读量:18 收藏

"我的心田本空无一物,你来之后万物生长,你走之后,一片荒芜"  --《隐入尘烟》


感谢这位兄弟:

https://www.yuque.com/docs/share/364ef08c-9405-45fe-b269-e9236de57242?#pBjVN, 

自己这样写主要是整理出来好查询,在加上自己遇到的问题和解决方案,以及汉化,题目发放的疑惑,值得写一下。

  • 主机:服务器版本的CentOs7

    链接:http:

  • Docker版本:20.10.2

  • Docker-compose版本:1.25.0

  • IP地址:公网地址或虚拟机地址

1.系统环境搭建配置:如果是阿里云服务器,直接更新yum源:

 yum update

2.如果是虚拟机不能直接更新yum源的话就换阿里源

centos 7 换阿里源方法:

  • 先下载  wget :一定要确保有wget

 yum install wget
  • 阿里源:

 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clean all
yum makecache
yum update

注:一定要运行yum update 确保软件的更新和下载

3.换源之后把openssh-server 进行安装,主要是为了方便命令的输入,和文件的传输

  • 运行:

yum  install openssh-server
  • 下载vim 编辑工具:

  yum install vim  
  • 配置/etc/sshd/sshd_config 不然xshell等工具不能连接,改过之后如图所示:

  • 用xshell连接就能复制命令,方便下面的操作:

4.安装系统环境所需服务,主要是python,数据库 运行完就ok了。

  • 运行:

 yum install -y git nginx mariadb mariadb-server Mysql-python python-pip gcc  python-devel yum-    utils device-mapper-persistent-data lvm2 epel-release

  • 安装docker之前换源

  • 命令:

yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  • 选择合适的版本并安装

  • 安装17.12.1.ce版本,可能很慢,耐心等待。

  • 运行:

yum -y install docker-ce-17.12.1.ce

5.DaoCloud配置docker镜像源加速

  • 运行:

curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
  • 查看是否安装成功并启动

  •    运行:

docker --version

6.安装docker-compose

# 下载docker compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# 添加可执行权限
chmod +x /usr/local/bin/docker-compose
# 将文件copy到 /usr/bin/目录下
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 查看版本
docker-compose --version

2.靶场环境

1.下载CTFd

  • 运行:

git clone https://github.com/glzjin/CTFd.git
  • 下载frp

  •       运行:

wget https://github.com/fatedier/frp/releases/download/v0.29.0/frp_0.29.0_linux_amd64.tar.gz
  • 下载完成后解压备用:

tar -zxvf frp_0.29.0_linux_amd64.tar.gz

2.下载ctfd-whale

  • 运行:

git clone https://github.com/glzjin/CTFd-Whale.git
  • 将下载的CTFd-Whale文件夹重命名为小写:

mv CTFd-Whale/ ctfd-whale

3.下载docker版本的frps

  • 运行:

git clone https://github.com/glzjin/Frp-Docker-For-CTFd-Whale
  • 将下载后的Frp-Docker-For-CTFd-Whale也重命名为小写:

  • 运行:

mv Frp-Docker-For-CTFd-Whale/ frp-docker-for-ctfd-whale
  • 下载之后截图:

   注:如果不能下载,或者下载缓慢的话,可以挂代理,也可以下载下来在传到服务器上 

3.CTFd环境配置

1.Docker集群设置 初始化docker集群

  • 运行:

docker swarm init

   如果出现这种情况Cannot connect to the Docker daemon at unix:

  • 运行:

systemctl start docker
  • 然后再运行初始化命令:

docker swarm init

2.将刚刚初始化的这个集群加入到节点当中

命令执行后,返回的就是节点ID了,暂时不用管这个节点ID

  • 运行:

docker node update --label-add='name=linux-1' $(docker node ls -q)

3.ctfd-whale放入CTFd的插件配置

  • 将ctfd-whale放入CTFd的插件目录中

  • 运行:

mv ctfd-whale/ CTFd/CTFd/plugins/
  • 进入目录并配置frps.ini文件

  • 运行:       

 cd frp-docker-for-ctfd-whale/frp
vim frps.ini

  改成图片所示:一般token不用更改,端口根据自己喜好更改,一般采用默认

4.修改完成后返回目录启动

注意:是 frp-docker-for-ctfd-whale目录

  • 运行:

 cd ..
docker-compose up -d

    耐心等待

  • 构建完成以后查看是否正在运行

 docker ps

5.将frpc文件配置到CTFd中

    首先进入CTFd目录中,新建frpc文件夹

    运行:  

 (1)cd CTFd/       (2) mkdir frpc
  • 进入frpc的目录(frp_0.29.0_linux_amd64)将里面的frpc,frpc.ini,frpc_full.ini,LICENSE这四个文件放在CTFd/frpc文件夹中

cd ../frp_0.29.0_linux_amd64
mv frpc.ini ../CTFd/frpc/
mv frpc_full.ini ../CTFd/frpc/
mv frpc ../CTFd/frpc/
mv LICENSE ../CTFd/frpc/
  • 进入刚刚新建的CTFd/fprc目录,配置frpc.ini文件,直接复制粘贴

[common]
token = randomme
server_addr = 172.1.0.4
server_port = 6490
pool_count = 200
tls_enable = true

admin_addr = 172.1.0.3
admin_port = 7400

注意:这里的token要和frp-docker-for-ctfd-whale/frp/frps.ini的token一样,前面没改,这里就是randomme

  • 进入CTFd目录,将下方内容复制到Dockerfile中,直接复制粘贴

FROM python:3.6-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories &&\
  apk update && \
  apk add python3 python3-dev linux-headers libffi-dev gcc make musl-dev py-pip mysql-client git openssl-dev g++
RUN adduser -D -u 1001 -s /bin/bash ctfd
WORKDIR /opt/CTFd
RUN mkdir -p /opt/CTFd /var/log/CTFd /var/uploads
RUN pip3 config set global.index-url https://pypi.doubanio.com/simple
RUN pip3 config set install.trusted-host pypi.doubanio.com
COPY requirements.txt .
RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple
COPY . /opt/CTFd
RUN for d in CTFd/plugins/*; do \
    if [ -f "$d/requirements.txt" ]; then \
      pip install -r $d/requirements.txt -i https://pypi.doubanio.com/simple; \
    fi; \
  done;
RUN chmod +x /opt/CTFd/docker-entrypoint.sh
RUN chown -R 1001:1001 /opt/CTFd
RUN chown -R 1001:1001 /var/log/CTFd /var/uploads
USER 1001
EXPOSE 8000
ENTRYPOINT ["/opt/CTFd/docker-entrypoint.sh"]
  • 在CTFd目录配置docker-compose.yml文件,直接复制粘贴

version: '2.2'

services:
ctfd-nginx:
  image: nginx:1.17
  volumes:
    - ./nginx/http.conf:/etc/nginx/nginx.conf  
  user: root
  restart: always
  ports:    
    - "443:443"
  networks:
      default:
      internal:
  depends_on:
    - ctfd
  cpus: '1.00'  
  mem_limit: 150M    
ctfd:
  build: .
  user: root
  restart: always
  ports:
    - "8000:8000"    
  environment:
    - UPLOAD_FOLDER=/var/uploads
    - DATABASE_URL=mysql+pymysql://root:[email protected]/ctfd
    - REDIS_URL=redis://cache:6379
    - WORKERS=1
    - LOG_FOLDER=/var/log/CTFd
    - ACCESS_LOG=-
    - ERROR_LOG=-
    - REVERSE_PROXY=true
  volumes:
    - .data/CTFd/logs:/var/log/CTFd
    - .data/CTFd/uploads:/var/uploads
    - .:/opt/CTFd:ro
    - /var/run/docker.sock:/var/run/docker.sock    
  depends_on:
    - db
  networks:
      default:
      internal:
      frp:
          ipv4_address: 172.1.0.2
  cpus: '1.00'    
  mem_limit: 450M    

db:
  image: mariadb:10.4
  restart: always
  environment:
    - MYSQL_ROOT_PASSWORD=ctfd
    - MYSQL_USER=ctfd
    - MYSQL_PASSWORD=ctfd
  volumes:
    - .data/mysql:/var/lib/mysql
  networks:
      internal:
  command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800, --log-warnings=0]
  cpus: '1.00'    
  mem_limit: 750M    

cache:
  image: redis:4
  restart: always
  volumes:
    - .data/redis:/data
  networks:
      internal:
  cpus: '1.00'    
  mem_limit: 450M    

frpc:    
  image: glzjin/frp:latest    
  restart: always
  volumes:
    - ./frpc:/conf/    
  entrypoint:
      - /usr/local/bin/frpc
      - -c
      - /conf/frpc.ini
  networks:
      frp:
          ipv4_address: 172.1.0.3  
      frp-containers:
  cpus: '1.00'    
  mem_limit: 250M    

networks:
  default:
  internal:
      internal: true
  frp:
      driver: bridge
      ipam:
          config:
              - subnet: 172.1.0.0/16
  frp-containers:
      driver: overlay
      internal: true
      ipam:
          config:
              - subnet: 172.2.0.0/16


  • 配置requirements.txt 这里主要是修改gevent版本号,gevent原本是1.4.0的,这边经过多次尝试,不指定版本号才能拉取,直接复制粘贴

Flask==1.1.1
Werkzeug==0.16.0
Flask-SQLAlchemy==2.4.1
Flask-Caching==1.4.0
Flask-Migrate==2.5.2
Flask-Script==2.0.6
SQLAlchemy==1.3.11
SQLAlchemy-Utils==0.36.0
passlib==1.7.2
bcrypt==3.1.7
six==1.13.0
itsdangerous==1.1.0
requests>=2.20.0
PyMySQL==0.9.3
gunicorn==19.9.0
normality==2.0.0
dataset==1.1.2
mistune==0.8.4
netaddr==0.7.19
redis==3.3.11
datafreeze==0.1.0
python-dotenv==0.10.3
flask-restplus==0.13.0
pathlib2==2.3.5
flask-marshmallow==0.10.1
marshmallow-sqlalchemy==0.17.0
boto3==1.10.39
marshmallow==2.20.2
gevent
tzlocal==2.1
  • 配置nginx,在CTFd的目录下,新建一个nginx文件夹并进入

mkdir nginx
cd nginx
  • 在上面创建的nginx目录下, 创建一个文件http.conf,输入以下内容,直接复制粘贴

worker_processes 4;
events {
worker_connections 1024;
}
http {
# Configuration containing list of application servers
upstream app_servers {
  server ctfd:8000;
}
server {
  listen 80;
  client_max_body_size 4G;
  # Handle Server Sent Events for Notifications
  location /events {
    proxy_pass http://app_servers;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
    proxy_buffering off;
    proxy_cache off;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $server_name;
  }
  # Proxy connections to the application servers
  location / {
    proxy_pass http://app_servers;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $server_name;
  }
}
}

6.开始构建,在CTFd目录下开始构建,运行下面的代码,耐心等待  

docker-compose up -d

拉取完镜像之后

  • 拉取完成后配置docker网络,先看一下现在的容器状态

docker ps -a

看到ctfd_frpc_1这个容器的状态是退出状态。

  • 查看docker的网络

docker network ls

7.需要将ctfd_frpc_1,frp-docker-for-ctfd-whale_frps_1,ctfd_ctfd_1这三个容器加入到ctfd_frp网络中    

并且这三个容器的IP如下:

 ctfd_ctfd_1:172.1.0.2

 ctfd_frpc_1:172.1.0.3

 frp-docker-for-ctfd-whale_frps_1:172.1.0.4

  • 查看一下ctfd_frp网络

docker network inspect ctfd_frp

我们需要把其他两个加进去

  • 只有ctfd_ctfd_1这个容器是在ctfd_frp网络里的,此时我们指定IP将其他两个容器加入ctfd_frp网络里

docker network connect --ip 172.1.0.3 ctfd_frp ctfd_frpc_1
docker network connect --ip 172.1.0.4 ctfd_frp frp-docker-for-ctfd-whale_frps_1
  • 再次查看一下ctfd_frp网络

  • 重启一下这两个容器

 docker restart ctfd_frpc_1 frp-docker-for-ctfd-whale_frps_1
  • 再次查看一下ctfd_frp网络

docker network inspect ctfd_frp

发现已经可以看到,这个时候,ctfd_frpc_1,frp-docker-for-ctfd-whale_frps_1,ctfd_ctfd_1这三个容器已经加入到ctfd_frp网络中了

  • 再查看一下ctfd_frpc_1容器如果没有一直重启就算配好了

  • 直接访问http://你的ip:8000

至此,靶场已经搭建好了

4.网站基础以及ctfd-whale配置

注:这个主要是配置题目分发时端口开启情况,因为你做一道题肯定是访问网站的一个端口

  • CTF配置

  • 管理员配置

  • 样式配置

  • 配置比赛开始时间

  • 直接finish 后

  • 进入管理员模板配置进行ctfd-whale配置

  • 设置方面,现给出下面的参数配置详情

属性配置
Docker API URLunix://var/run/docker.sock
Frp API IPfrpc的ip配置 172.1.0.3
Frp API Portfrpc的端口配置 7400
Frp Http Domain SuffixDocker API URL to connect(可填None)
Frp Http Port80
Frp Direct IP Address你的公网ip,本机即为127.0.0.1
Frp Direct Minimum Port与之前frps最小端口呼应
Frp Direct Minimum Port与之前frps最大端口呼应
Max Container Count不超过最大-最小
Max Renewal Times最大实例延时次数
Frp config template填入frps的配置,只需填[common]
Docker Auto Connect Containersctfd_frpc_1
Docker Dns Setting可填机器内DNS,没有可填个外网DNS114.114.114.114
Docker Swarm Nodeslinux-1 与前面swarm集群呼应
Docker Multi-Container Network Subnet内网题大子网ip配置/CIDR
Docker Multi-Container Network Subnet New Prefix每个内网题实例的CIDR
Docker Container Timeout单位为秒
Docker Auto Connect Networkctfd_frp-containers
  • 其中Frp config template配置内容如下,一会直接复制粘贴就行

[common]
token = randomme
server_addr = 172.1.0.4
server_port = 6490
pool_count = 200
tls_enable = true

admin_addr = 172.1.0.3
admin_port = 7400
  • 我的配置

5.汉化

  • 网上按照教程一顿操作,弄一个,网站瘫痪一次。最后发现汉化版本不一样,本次CTFd 的版本是2.3.1 因此,直接去github搜一下,果然找到了一个,只是一个半汉化。先凑合着用呗,其实你也可以自己汉化,就是把里面的样式自己改一下,比如在标题那里英文改成中文。

1.汉化链接

https://github.com/cxaqhq/ctfd-2.3.1

汉化包:ctfd-2.3.1-master.zip

下载下来之后,直接把

这两个替换为

这两个

2.汉化效果

6.发放题目

注:因为是swam模式本次仅仅测试了dynamic_ docker来添加题目 ,其他形式,请自测

1.添加一道web题目,如图这样配置

2.完成之后测试如下

因为拉取镜像需要时间,请耐性等待,多刷新浏览器

4.测试 网址

5.有的人可能就会问了,那我如何获取其他的题目,接下来就讲一讲。

  • 其实也没有啥,比如ctftraining/qwb_2019_supersqli 就是拉取了ctftraining的一道题目。我在CTFd源码里面没有找到,去github找了一下,发现了这个。

 https://github.com/CTFTraining/CTFTraining
  • 然后我尝试拉取其他的题目,比如 qwb_2019_upload 按照格式ctftraining/qwb_2019_upload进行拉取,果然成功了。大约6-8分钟拉取完一个题目,请耐心等待。

至于其他方式拉取题目我也测试了下,因为是swam模式,只有docker-dynamic这种方式比较简单。

  • standard 模式拉取ctftraining的试题也是可行的

   比如我在~目录下创建一个CTFTraining目录,git 一道题目

mkdir CTFTraining
cd CTFTraining/
git clone https://github.com/CTFTraining/0ctf_2016_unserialize.git
cd cd 0ctf_2016_unserialize/
  • 然后修改docker-compose.yml

  • 修改之后,启动

  • 这个时候你访问靶机地址:http://你的ip:8302   就能访问

  • 同样在CTFd中加入这道题目

[是兄弟就来打我](http://192.168.52.164:8302/)
  • 按照你自己的需求进行更改

flag  就是docker-compose.yml里面的flag

但是这种方式不太好,你打完这道题之后提交flag ,这道题目依然开着,只能手动关闭

6.附录:近年ctf writeup大全

https://github.com/ctfs/write-ups-2016

https://github.com/ctfs/write-ups-2015

https://github.com/ctfs/write-ups-2014

fbctf竞赛平台Demo

https://github.com/facebook/fbctf

ctf Resources

https://github.com/ctfs/resources
https://github.com/ctfwiki/ctf_game_history
https://github.com/SecWiki/ctf-hub
https://github.com/le31ei/ctf_challenges

7.问题报错以及解决办法

注释:本人遇到的问题还算比较少,因此报错以及解决办法多是搬运他人写的。

1.frpc日志报错:

解决方案:

仔细配置网络,网络配置的要求如下:

  • 将ctfd_frpc_1,frp-docker-for-ctfd-whale_frps_1,ctfd_ctfd_1这三个容器加入到ctfd_frp网络中

  • 这三个容器对应的IP如下:

    • ctfd_ctfd_1:172.1.0.2

    • ctfd_frpc_1:172.1.0.3

    • frp-docker-for-ctfd-whale_frps_1:172.1.0.4

docker network connect --ip 172.1.0.3 ctfd_frp ctfd_frpc_1
docker network connect --ip 172.1.0.4 ctfd_frp frp-docker-for-ctfd-whale_frps_1
docker restart ctfd_frpc_1 frp-docker-for-ctfd-whale_frps_1

加入ctfd_frp网络之后,重启完成之后再查看一下ctfd_frp网络,运行

 docker network inspect ctfd_frp

如果还是不行,灵活使用docker 的logs功能,查看是什么问题报错

docker logs <容器ID或名称>

2.Dockerfile的gevent报错

如果是以下报错,可以通过修改版本号解决

解决方案:

其他Dockerfile在构建的时候可能会出现gevent构建不成功的问题,有以下几种解决办法:

  1. 将镜像源替换为清华源

  2. 替换gevent版本

  3. 不指定gevent版本号

在CTFd目录下,修改requirements.txt的gevent

然后 docker-compose down 再启动 docker-compose up -d  不出意外应该就能解决问题

3.Dockerfile的world模块报错

解决方案:

将Dockerfile中的python和python-dev删掉,或者修改成python2或python3,python2-dev或python3-dev都可以

4.构建的时候,ctfd镜像无法启动

解决方案:

将docker-entrypoint.sh第一行修改为

#!/bin/bash

修改后重新docker-compose up -d即可

5.ctfd_ctfd_1容器一直在重启:

 ctfd_ctfd_1日志报错如下

解决方法:

出现时区问题,因此在requirements.txt中添加如下:

tzlocal==2.1

6.ctf_frpc_1容器一直重启

解决方案:

仔细配置网络,网络配置的要求如下:

  • 将ctfd_frpc_1,frp-docker-for-ctfd-whale_frps_1,ctfd_ctfd_1这三个容器加入到ctfd_frp网络中

  • 这三个容器对应的IP如下:

    • ctfd_ctfd_1:172.1.0.2

    • ctfd_frpc_1:172.1.0.3

    • frp-docker-for-ctfd-whale_frps_1:172.1.0.4

docker network connect --ip 172.1.0.3 ctfd_frp ctfd_frpc_1
docker network connect --ip 172.1.0.4 ctfd_frp frp-docker-for-ctfd-whale_frps_1
docker restart ctfd_frpc_1 frp-docker-for-ctfd-whale_frps_1

重启完成之后再查看一下ctfd_frp网络

docker network inspect ctfd_frp

如果还是不行,灵活使用docker 的logs功能,查看是什么问题报错

docker logs <容器ID或名称>

如果是以下报错

则重新配置frpc,编辑/CTFd/frpc/frpc.ini

[common]
token = randomme
server_addr = 172.1.0.4
server_port = 6490
pool_count = 200
tls_enable = true

admin_addr = 172.1.0.3 #这里千万千万别忘加,之前要被搞气死
admin_port = 7400

然后运行

docker restart ctfd_frpc_1

7.MySQL容器反复重启

解决方法:

因为当前目录下的.data文件中的MySQL日志与容器版本不匹配

解决方法是将.data文件删除,重新构建镜像

7.docker容器无法启动或者frp端口无法映射

如果docker容器无法启动或者frp端口无法映射可以进容器检查,确保docker api填写正确,如docker-compose.yml中写的unix:///var/run/docker.sock你也可以使用端口形式的api如官方示例:可以用IP:端口指定API

docker容器无法启动问题

进入容器检查:

docker exec -it <ctfd容器id> sh
/opt/CTFd# python

>>>import docker
>>>client=docker.DockerClient(base_url="unix:///var/run/docker.sock")
>>>client.images.list()

如果api正确会列出所有镜像

frp端口无法映射

其实检查可以顺便检查一下上面的,因为都在ctfd容器内 docker exec -it <ctfd容器id> sh

docker exec -it <ctfd容器id> sh
/opt/CTFd# python
>>>import requests
>>>requests.get("http://172.1.0.3:7400/api/reload")//即frp api的地址

返回
<Response [200]> #表示成功

如果frpc还是出现如下问题

requests.exceptions.ConnectionError: HTTPConnectionPool(host='172.1.0.3', port=7400): Max retries exceeded with url: /api/reload (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f8df919f850>: Failed to establish a new connection: [Errno 111] Connection refused'))

则重新配置frpc,编辑/CTFd/frpc/frpc.ini

[common]
token = randomme
server_addr = 172.1.0.4
server_port = 6490
pool_count = 200
tls_enable = true

admin_addr = 172.1.0.3 #这里千万千万别忘加,之前要被搞气死
admin_port = 7400

然后运行docker restart ctfd_frpc_1(这里再看frpc.ini会发现内容更新了,admin配置没了,不用担心)

再进容器检查requests.get("http://172.1.0.3:7400/api/reload")应该就可以了


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5MDY2MTUyMA==&mid=2247488977&idx=1&sn=d2f1d775ecd2b1dfe20078a769e4758d&chksm=cfd86a2ef8afe338d1ef2fc6d86a73489742bdbd47cd63c86e51815e5addab837ba5b2cd0c9a#rd
如有侵权请联系:admin#unsafe.sh