CTF需要web环境,通过docker可以更好更快地创建一个独立的容器,作为选手的动态靶机。
Docker,是具备有简化配置、全平台、提高效率、方便共享、快速部署等特点的。
去年学了docker,一直想用它来创建一个本地web环境。
正好赶上了出题时机,顺便总结一下近期的docker笔记。
一、Docker基本命令
纯笔记,其实也已经够用了。 详细资料请移步下方链接🔗
拉取查看篇
1 2 3 4 5 6 7 8
| docker pull ubuntu:16.04 # 拉取对应镜像
docker images # 查看当前系统中存在镜像
docker ps # 查看运行中的容器 docker ps -a # 查看所有容器(包括已结束运行的)
docker cp 主机文件名 容器id:容器指定目录 # 将主机文件复制到指定位置,反之则交换位置
|
运行篇
1 2
| 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: 绑定一个卷
停止删除篇
1 2 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 # 删除指定镜像
|
进入退出容器篇
1 2 3
| docker exec -it 容器id /bin/bash # 进入容器的bash界面 docker attach 容器id # 进入容器内部 exit # 进入docker内部后退出容器
|
之前的docker exec -it ID /bin/bash
其实并没有进入这个容器,而是弹了一个bash
出来让我们能在容器里操作,而attach
才算进入了容器内部。
查看相关配置
1 2 3 4
| docker port 容器id # 查看容器映射端口 netstat -tlnp # 查看主机开放端口 systemctl status firewalld # 查看防火墙状态 systemctl stop firewalld # 暂时关闭防火墙
|
导出导入篇
1 2 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
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
| 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 文件的存放目录下,执行构建动作。
1 2 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
1 2 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
就是直接对文本文件进行操作的
替换文件中的原字符串
1 2 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
1 2 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命令
1 2 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只需要这么写:
1 2 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也拥有多个版本的操作系统与题目环境。
如果还想通过更多的环境学习如何出题,可以在这个 👉 项目 👈 中查看更多的题目。