如何优雅的使用Docker出一道动态flag的CTF题-GZCTF

最近想给新生出一些CTF入入入门题,一点一点摸索搭建了GZCTF(感谢GZTime师傅制作的如此优秀的平台),但是苦于出题,网上也没有很多相关文章,只能自己一点一点摸索。

关于安装Docker不在赘述,本文侧重记录构建Docker镜像实现动态flag的一种解决办法

首先一道CTF赛题的镜像构建前的大致目录结构如下-以SQL注入题目为例

CTF_Challenge/
├── Dockerfile # 提示信息
├── src/ # 题目源代码、脚本或者应用程序
│ ├── db.sql # 数据库初始化脚本
│ ├── index.html # Web应用程序的主页
│ ├── sql_connect.php # 数据库连接配置
│ ├── flag.sh # 动态flag的初始化

这是一个最简单sql注入题目的目录,不包含README,Wreitup等。

关于index.php和sql_connect.php两个文件不在赘述。

Dockerfile

这里采用公开镜像base_image_nginx_mysql_php_56,师傅还有很多很好的镜像,例如php7的,可以移步这里CTFTraining

这个镜像预先集成了nginx,mysql等必要环境,无需我们手动构建,给我们构建Dockerfile带来的极大的方便

使用此镜像的Dockerfile的示例

FROM ctftraining/base_image_nginx_mysql_php_56

COPY src /var/www/html

# 自定义 flag 方式
# COPY flag.sh /flag.sh

下面是我的Dockerfile

FROM ctftraining/base_image_nginx_mysql_php_56

COPY src /var/www/html

RUN mv /var/www/html/flag.sh / && \
chmod +x /flag.sh

实现动态flag需要在flag.sh中完成,不能在Dockerfile中实现,这是一个很简单的问题,因为Dockerfile是构建镜像的,注入动态flag需要在分发构建好的镜像后,不同用户启动镜像时完成。

db.sql

使用base_image_nginx_mysql_php_56构建镜像时,将db.sql放在src目录下,镜像启动过程中db.sql会自动执行并初始化数据库。

所以这个文件就是初始化数据库用的,不过对题目而言,sql注入题目需要在数据库中预先创建一个放置flag的表,用于存放flag。

比如

DROP DATABASE IF EXISTS challenge;
CREATE DATABASE challenge CHARACTER SET utf8 COLLATE utf8_general_ci;
USE challenge;
CREATE TABLE flag (
flag_is_here varchar(50)
);

这样就在数据库challenge中初始化了一个flag的表,其中有一个flag_is_here的字段

flag.sh

同样使用base_image_nginx_mysql_php_56构建镜像时,将flag.sh放在src目录下,镜像启动过程中会自动执行,需要在Dockerfile中给予可执行权限。下面是在数据库中插入动态flag值得一种方案

#!/bin/sh

echo "INSERT INTO flag (flag_is_here) VALUES ('$GZCTF_FLAG');" >> /var/www/html/db.sql
export GZCTF_FLAG=""

值得注意的是,镜像构建的时候先执行Dockerfile,然后执行flag.sh,最后执行db.sql

所以不能在flag.sh中这样写

#!/bin/sh

mysql -uroot -proot -e "USE challenge;INSERT INTO flag (flag_is_here) VALUES ('$GZCTF_FLAG');"
export GZCTF_FLAG=""

因为此时db.sql还未执行,会出现报错”ERROR 1049 (42000) at line 1: Unknown database ‘challenge’”

完成这些后,就可以构建镜像查看效果了

docker build -t 镜像名 .	   # 构建镜像

记住构建的镜像名,可以选择把他push到dockrhub上方便下载和分发

然后在GZCTF的题目镜像中填入构建好的镜像名,选择开启测试容器

image-20240104180447177

image-20240104180538740

然后就可以访问实例入口,尝试解题,看看flag是否已经被替换为动态flag

image-20240104180643452

其他的flag.sh写法

关于其他类型的ctf题目,比如flag存放在根目录下的flag.txt,或者在源码目录的flag.php中等等以纯文本形式存放的,flag.sh可以参考如下

#!/bin/sh
sed -i "s/flag{test}/$GZCTF_FLAG/" /flag.txt
export GZCTF_FLAG=""

预先在需要存放flag的位置写一个flag{test},然后用sed,将环境变量替代他即可,最后记得把环境变量置空防止非预期。