CTF需要web环境,通过docker可以更好更快地创建一个独立的容器,作为选手的动态靶机。
Docker,是具备有简化配置、全平台、提高效率、方便共享、快速部署等特点的。
去年学了docker,一直想用它来创建一个本地web环境。
正好赶上了出题时机,顺便总结一下近期的docker笔记。
一、Docker基本命令
纯笔记,其实也已经够用了。 详细资料请移步下方链接🔗
拉取查看篇
| 12
 3
 4
 5
 6
 7
 8
 
 | docker pull ubuntu:16.04                # 拉取对应镜像
 docker images		                # 查看当前系统中存在镜像
 
 docker ps          	                # 查看运行中的容器
 docker ps -a       	                # 查看所有容器(包括已结束运行的)
 
 docker cp 主机文件名 容器id:容器指定目录  # 将主机文件复制到指定位置,反之则交换位置
 
 | 
运行篇
| 12
 
 | docker run -it 容器id		      # 以交互模式运行容器,为容器重新分配一个伪输入终端docker run -d 容器id		      # 后台运行容器,并返回容器ID
 
 | 
docker run命令详情
    | 1
 | docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
 | 
OPTIONS说明:
- -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
- -d: 后台运行容器,并返回容器ID;
- -i: 以交互模式运行容器,通常与 -t 同时使用;
- -P: 随机端口映射,容器内部端口随机映射到主机的端口
- -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
- -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
- –name=“nginx-lb”: 为容器指定一个名称;
- –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
- –dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
- -h “mars”: 指定容器的hostname;
- -e username=“ritchie”: 设置环境变量;
- –env-file=[]: 从指定文件读入环境变量;
- –cpuset=“0-2” or --cpuset=“0,1,2”: 绑定容器到指定CPU运行;
- -m :设置容器使用内存最大值;
- –net=“bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
- –link=[]: 添加链接到另一个容器;
- –expose=[]: 开放一个端口或一组端口;
- –volume , -v:	 绑定一个卷
停止删除篇
| 12
 3
 4
 5
 6
 7
 
 | docker stop 容器id 	            #停止指定容器docker stop $(docker ps -a -q)      #停止所有已经运行的容器
 
 docker rm 容器id   	            #删除指定容器,删除前先停止
 docker rm $(docker ps -a -q)        #删除所有已经停止的指令
 
 docker rmi 镜像id		    # 删除指定镜像
 
 | 
进入退出容器篇
| 12
 3
 
 | docker exec -it 容器id  /bin/bash  	# 进入容器的bash界面docker attach 容器id			# 进入容器内部
 exit                                    # 进入docker内部后退出容器
 
 | 
之前的docker exec -it ID /bin/bash其实并没有进入这个容器,而是弹了一个bash出来让我们能在容器里操作,而attach才算进入了容器内部。
查看相关配置
| 12
 3
 4
 
 | docker port 容器id   				# 查看容器映射端口netstat -tlnp					# 查看主机开放端口
 systemctl status firewalld			# 查看防火墙状态
 systemctl stop firewalld			# 暂时关闭防火墙
 
 | 
导出导入篇
| 12
 3
 4
 
 | docker export 容器id > ctf.tar			# 导出为tar文件docker save 镜像name  > ./ctf.tar		# 将→指定镜像名←保存成 tar 归档文件
 cat ctf.tar | docker import - ctf               # 可以这样导入
 docker load<ctf.tar			        # 载入镜像包
 
 | 
小Tip:
- 
docker save 保存的是镜像(image),docker export 保存的是容器(container); 
- 
docker load 用来载入镜像包,docker import 用来载入容器包,但两者都会恢复为镜像; 
- 
docker load 不能对载入的镜像重命名,而 docker import 可以为镜像指定新名称。 
若导入时出现这个问题open /var/lib/docker/tmp/docker-import-970689518/bin/json: no such file or directory,说明这个tar包缺少docker所需要的一些json文件,不能直接导入。
二、文件目录总览
目录文件夹如下:

三、Dockerfile
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
👉 Dockerfile相关指令教程 👈
文件名:Dockerfile
| 12
 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
 
 | FROM ubuntu:16.04
 LABEL Author="Ztop"
 LABEL Blog="https://www.zeker.top"
 ENV REFRESHED_AT 2022-03-01
 
 ENV LANG C.UTF-8
 
 
 
 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 && \
 apt-get update -y
 
 
 
 ENV DEBIAN_FRONTEND noninteractive
 
 
 RUN apt-get install apache2 -y
 
 
 RUN apt-get install php -y
 RUN apt-get install libapache2-mod-php -y  --fix-missing
 RUN apt-get install php7.0-mysql -y
 
 
 
 
 
 
 
 
 
 
 RUN rm -rf /var/www/html/index.html
 COPY ./src/ /var/www/html/
 
 WORKDIR /var/www/html/
 
 
 COPY ./run.sh /root/run.sh
 RUN  chmod +x /root/run.sh
 ENTRYPOINT ["/root/run.sh"]
 
 
 
 
 
 EXPOSE 80
 
 | 
在 Dockerfile 文件的存放目录下,执行构建动作。
| 12
 3
 4
 5
 6
 7
 8
 
 | docker build -t ctf:v1 .            # dockerfile目录下输入构建命令,用来构建基于ubuntu:16.04的自定义镜像。
 docker run -d -p 5555:80 -e FLAG=flag{$(cat /proc/sys/kernel/random/uuid)} 镜像id  # 添加系统uuid作为动态flag字段
 
 docker ps                           # 查看是否运行
 
 docker exec -it 容器id /bin/bash     # 进入容器
 exit                                 # 在容器内退出
 
 | 
四、Shell文件
文件名:run.sh
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | #! /bin/bash
 
 uuid=$(cat /proc/sys/kernel/random/uuid)
 FLAG="flag{$uuid}"
 sed -i "s/flag_is_here/$FLAG/" /var/www/html/flag
 
 
 
 export FLAG=not_flag
 FLAG=not_flag
 
 
 service apache2 restart
 
 
 tail -f /dev/null
 
 | 
附上flag文件中的内容
sed -i 就是直接对文本文件进行操作的
替换文件中的原字符串
| 12
 3
 
 | sed -i 's/原字符串/新字符串/' /home/1.txt		# 替换每一行的第一个匹配项
 sed -i 's/原字符串/新字符串/g' /home/1.txt		# 替换每一行的所有匹配项
 
 | 
五、Docker-compose
Docker-compose 是一个用来把 docker 自动化的东西。
有了 docker-compose 你可以把所有繁杂重复的 docker 操作全都一条命令,自动化的完成。
Compose 是用于定义和运行多容器 Docker 应用程序的工具。
通过 Compose,你可以使用 YML 文件来配置应用程序需要的所有服务。
然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
文件名:docker-compose.yml
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | version: '3'
 
 services:
 web1:
 
 image: ctf/test1
 build: ./web/
 
 ports:
 - "5555:80"
 environment:
 - FLAG=flag{this_is_test_flag}
 
 
 | 
- version
 说明了yml文件指明的版本号,一般我们使用2,3这两个版本。
- services
 yml文件的主体,定义了服务了配置。里面的web标签是我们自己定义的。
 build表明了以dockerfile类型启动一个容器,后面跟的是dockerfile的路径,支持相对路径和绝对路径,在这个yml文件里面,表明dockerfile与yml处在同个目录下。
容器的启动可以根据已有的镜像,如果定义了image这个标签,就会从本地搜寻相关镜像构建容器,如果本地找不到相关的镜像,就会从网上数据库搜寻相关的镜像。
但大家可能会产生疑问了,这里我们定义了build还有image两个不同的标签来构建镜像,那么容器到底要用build还是image来构建呢,这种情况下将按照dockerfile的方式来构建镜像,并且把镜像的名称定义为image标签里面的名称。
- environment
 构建了相关的环境变量,在这里我们是定义了一个FLAG环境变量,并且值为flag{this_is_test_flag}
 定义之前要删除shell文件中的定义变量,否则会报错
- ports
 定义了映射的端口,5555:80表示映射到本机的5555端口,后面的80端口则要与Dockerfile文件中EXPOSE的端口保持一致。
编写完成,直接运行下面第一条compose命令
| 12
 3
 
 | docker-compose up  -d   # 创建并启动所有服务(常用)docker-compose build    # 容器镜像创建(没启动)
 docker-compose stop     # 关闭所有服务
 
 | 
此时访问主机的5555端口,即http://your-ip:5555,即可看到题目👇

查看一下是否实现本地动态flag👇

六、好用的出题镜像
从上面的过程中,我们看到对于一道题目来说,除了源码以外,最大的不方便之处就是还要有相关的一些文件配置。
在这里我推荐virink写的base_image_nginx_mysql_php_56来辅助我们快速出题。
这个镜像主要好在不需要我们去配置其他的nginx设置,而且还支持自动导入db.sql文件,支持自动执行flag.sh文件。
这里以一道题为例,题目中主要是需要配置数据库.
我们只需要src文件夹中放入源码、flag.sh、db.sql(文件名不能变),现在我们dockerfile只需要这么写:
| 12
 3
 4
 5
 6
 
 | FROM ctftraining/base_image_nginx_mysql_php_56
 COPY src /var/www/html
 
 RUN mv /var/www/html/flag.sh / \
 && chmod +x /flag.sh
 
 | 
这个镜像就会自动为我们配置相关的nginx文件,同时自动导入要执行的db.sql文件来配置数据库(默认用户为root,密码也为root,执行后db.sql会被删掉)
同时镜像还会自动执行flash.sh来从环境变量中读取flag写入到flag.php中(需要注意的是,flag.sh必需在根目录下,也就是我们需要执行mv /var/www/html/flag.sh /这一步的原因所在)。
可以看到,这个镜像极大的方便了我们对Dockerfile的编写,把Dockerfile简化到只需要两三句话就能搞定。
如果是要在php7的环境下出题,可以采用base_image_nginx_mysql_php_73
更多docker模板可前往链接 👉 https://github.com/DASCTF-Test 👈 ,这个docker模板中囊括了很多比赛题目需要的环境,web方面拥有多个版本php mysql的环境,拥有java与python等题目环境,pwn也拥有多个版本的操作系统与题目环境。
如果还想通过更多的环境学习如何出题,可以在这个 👉 项目 👈 中查看更多的题目。