GZCTF平台搭建
环境:
1 2 3 4
| Ubantu22.04.1 Docker 24.0.7 docker-compose 1.29.2 GZCTF 0.18
|
GZCTF官方文档: https://docs.ctf.gzti.me/zh/quick-start
一. 安装Docker和Docker-compose
1 2
| sudo apt update sudo apt install docker.io docker-compose
|
执行命令后, 检查是否成功安装
1 2
| docker -v docker-compose -v
|
二. 部署GZCTF
0x01.创建GZCTF部署目录
1 2 3
| cd / mkdir GZCTF cd GZCTF
|
0x02. 编辑GZCTF配置文件appsettings.json, docker-compose.yml并保存于 /GZCTF 目录下
appsettings.json:
1 2 3
| <Your POSTGRES_PASSWORD>设置为数据库密码 <Your PUBLIC_ENTRY> 需改为外部访问地址 <Your XOR_KEY> 设置为用于加密比赛私钥的随机字符串(任意填写即可)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| { "AllowedHosts": "*", "ConnectionStrings": { "Database": "Host=db:5432;Database=gzctf;Username=postgres;Password=<Your POSTGRES_PASSWORD>" }, "EmailConfig": { "SendMailAddress": "a@a.com", "UserName": "", "Password": "", "Smtp": { "Host": "localhost", "Port": 587 } }, "XorKey": "<Your XOR_KEY>", "ContainerProvider": { "Type": "Docker", "PortMappingType": "Default", "EnableTrafficCapture": false, "PublicEntry": "<Your PUBLIC_ENTRY>", "DockerConfig": { "SwarmMode": false, "Uri": "unix:///var/run/docker.sock" } }, "RequestLogging": false, "DisableRateLimit": true, "RegistryConfig": { "UserName": "", "Password": "", "ServerAddress": "" }, "CaptchaConfig": { "Provider": "None", "SiteKey": "<Your SITE_KEY>", "SecretKey": "<Your SECRET_KEY>", "GoogleRecaptcha": { "VerifyAPIAddress": "https://www.recaptcha.net/recaptcha/api/siteverify", "RecaptchaThreshold": "0.5" } }, "ForwardedOptions": { "ForwardedHeaders": 5, "ForwardLimit": 1, "TrustedNetworks": ["192.168.12.0/8"] } }
|
:
1 2 3 4 5
| <Your POSTGRES_PASSWORD> 设置为数据库密码, 与上文json文件的一致即可 <Your GZCTF_ADMIN_PASSWORD> 设置为初始管理员密码, 部署成功后默认管理员账户为admin, 密码为该处值 ports: - "80:8080"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| version: "3.0" services: gzctf: image: registry.cn-shanghai.aliyuncs.com/gztime/gzctf:develop restart: always environment: - "GZCTF_ADMIN_PASSWORD=<Your GZCTF_ADMIN_PASSWORD>" - "LC_ALL=zh_CN.UTF-8" ports: - "80:8080" volumes: - "./data/files:/app/files" - "./appsettings.json:/app/appsettings.json:ro" - "/var/run/docker.sock:/var/run/docker.sock" depends_on: - db
db: image: postgres:alpine restart: always environment: - "POSTGRES_PASSWORD=<Your POSTGRES_PASSWORD>" volumes: - "./data/db:/var/lib/postgresql/data"
|
0x03. 启动GZCTF
但是启动之前需要换源,因为未知原因国内大部分docker镜像站都不可用了
三. docker走本机代理
1 2 3 4 5
| vi /etc/docker/daemon.json { "registry-mirrors": ["https://hub.docker.com/"] } 官方源
|
修改 Docker 代理配置
修改配置文件(如果没有就创建)
1 2 3
| mkdir /etc/systemd/system/docker.service.d touch /etc/systemd/system/docker.service.d/proxy.conf vim /etc/systemd/system/docker.service.d/proxy.conf
|
按照以下模板修改代理的 IP 和 端口号信息:
1 2 3
| [Service] Environment="HTTP_PROXY=http://192.168.31.1:7890" Environment="HTTPS_PROXY=http://192.168.31.1:7890"
|
加载配置,重启 Docker
1 2 3 4 5 6
| systemctl daemon-reload
systemctl restart docker
systemctl show --property=Environment docker
|
到这里你已经成功了99%接下来
1 2
| docker-compose up -d docker ps
|
你会看到这两个容器
四. 进入靶场, 登陆管理员
1 2
| 账户:admin 密码:<Your GZCTF_ADMIN_PASSWORD>设定的值
|
五. 创建比赛, 编辑题目
0x01. Web动态Flag题目部署
01. 创建如下结构目录(这里只做一个演示)
1 2 3 4
| +-- src | +-- index.php | +-- flag.sh +-- Dockerfile
|
接下来分别讲解每个文件的用处
02. index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php error_reporting(0); highlight_file(__FILE__); ini_set('open_basedir', '/var/www/html/');
if(isset($_GET['cmd'])&&$cmd = $_GET['cmd']){ if (!preg_match('/flag|phpinfo|system|exec|passthru|shell_exec|ob_start|echo|popen|proc_open|fopen|popen|base64|\<|\>|{|}|\^|\~|\$|\`|\.|\*|\?|\'|\"/im', $cmd)) { eval($cmd); }else{ echo '达咩!!!'; } }else{ echo '你要毁了网络安全吗?'; } ?>
|
03. flag.sh
1 2 3 4 5
| #!/bin/sh echo "flag{testflag}" > /flag sed -i "s/flag{testflag}/$GZCTF_FLAG/" /flag export GZCTF_FLAG="" rm -rf /flag.sh
|
官方文档中提到, 容器采用 $GZCTF_FLAG 环境变量进行注入(通俗易懂的讲, 就是GZCTF会生成一个Flag放在 $GZCTF_FLAG 环境变量中), 所以我们只要把 $GZCTF_FLAG 放到我们想放的位置就可以了
所以就采用如上命令, 把预先留的flag{testflag}替换为 $GZCTF_FLAG 的值
export GZCTF_FLAG=””这一句的用处, 我觉得是, 为了防止从环境变量中得到Flag, 从而绕过了原本题目
踩坑:
当时我是利用windows系统创建的flag.sh文件然后再拖入到ubuntu中的,结果每次flag.sh文件的执行都有各种问题,解决方案就是在ubuntu中创建,然后把内容复制到ubuntu的flag.sh文件中就可以了
04. Dockerfile
1 2 3 4 5
| FROM ctftraining/base_image_nginx_php_73
COPY src /var/www/html
RUN mv /var/www/html/flag.sh / && chmod +x /flag.sh
|
上文选用了 ctftraining/base_image_nginx_php_73 来作为基础镜像, 该镜像可以自动运行flag.sh帮助实现动态Flag, 关于该镜像的更多信息可以通过如下仓库
1
| https://github.com/CTFTraining/base_image_nginx_php_73
|
这里再提一句, GZCTF中, Dockerfile不需要写 EXPOSE 来暴露端口, 直接在Web页面创建题目时填写即可, 如果写了反而会导致占用端口资源等问题
05. 创建镜像
1 2
| 在Dockerfile所在目录下, 运行 docker build -t name/web1 .
|
name为dockerhub名(当然如果你不push到DockerHub上这里乱写也行)
testweb为镜像名(自定义)
“.”为版本号,代表latest
1. 查看镜像
3.添加代理
想要push到仓库,我们如果虚拟机是nat模式就需要配置代理
1 2 3 4 5 6 7
| sudo su 通过 vim /etc/environment 编辑文件,添加如下内容: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
export http_proxy="http://192.168.31.1:7890" export https_proxy="https://192.168.31.1:7890" export socks_proxy="socks5://192.168.31.1:7890"
|
这里的 ip 就是 VMnet8 的 ip,端口是代理的端口
通过 curl www.google.com 测验代理是否成功
2. push镜像
1 2
| docker login docker push name/web1
|
0x01. 进入GZCTF平台, 部署题目
01. 创建比赛
02. 创建题目时, 选择动态容器
03. 设置容器镜像
容器镜像填入刚刚我们push上去的地址(即上文框出的地址, name/web1),
服务端口根据传入的容器需求填写(即容器对外开放的端口)
剩下的参数自行决定
04. 设置Flag生成规则
点击上文界面的编辑附件及flag
如果没有需求, 直接这样也可以, 有更多需求可以查看官方文档
六. 如果需要连接数据库
由于镜像
1
| ctftraining/base_image_nginx_mysql_php_73
|
内置mysql并且已经和初始化存在一个ctftraining表供我们利用
我们只需要对应添加flag进数据库即可
1 2 3 4 5 6
| flag.sh
mysql -e "USE ctftraining; CREATE TABLE IF NOT EXISTS \`FLAG_TABLE\` (\`FLAG_COLUMN\` varchar(100) NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;INSERT INTO \`FLAG_TABLE\` VALUES ('$GZCTF_FLAG');" -uroot -proot export GZCTF_FLAG="" rm -rf /flag.sh
|
1 2 3 4 5 6 7 8 9 10 11
| Dockerfile
FROM ctftraining/base_image_nginx_mysql_php_73
COPY src /var/www/html
RUN mv /var/www/html/flag.sh / \ && chmod +x /flag.sh \ && chown -R www-data:www-data /var/www/html \ && sed -i 's/skip-network/#skip-network/' /etc/my.cnf.d/mariadb-server.cnf
|
七. 常用docker命令
0x01. docker重启容器
1 2 3 4 5
| docker restart 名称/ID docker start 名称/ID docker kill 名称/ID docker-compose restart 名称/ID 重启
|
0x02. docker删除镜像
1 2
| docker rmi 名称/ID docker rmi -f 名称/ID
|
0x03. docker停止并删除容器
1 2 3 4
| docker stop 名称/ID docker rm 名称/ID docker stop $(docker ps -aq) docker rm $(docker ps -aq)
|
0x04. docker打包镜像
1
| docker save 镜像名 > xxx.tar
|
0x05. docker解压镜像
1
| docker load -i image.tar
|
0x06. docker启动镜像
1
| docker run -d -p 8080:80 -e GZCTF_FLAG=flag{111} image_name
|
0x07. docker进入到容器内部
1 2 3 4 5
| docker exec -it 容器id /bin/sh 退出exit
docker exec -it 容器id /bin/sh 退出ctrl + D
|
参考文章:
VMware、Docker - 让虚拟机走主机代理,解决镜像封禁问题
2024年7月,解决国内无法拉取docker官方镜像问题
GZCTF靶场从零开始搭建教程(一)>>单机部署
GZCTF靶场从零开始搭建教程(二)>>动态Flag题目部署+本地上传Docker镜像部署