Docker是什么

在没有Docker之前 如果你要在电脑上跑一个别人写的 CTF Web 题目 要在自己电脑上装一堆符合的环境 Docker 就是一个极其轻量级的“虚拟机”替代方案 两个最核心的概念
镜像 (Image) 它是一个只读的文件,里面打包好了操作系统基础、运行环境(比如 PHP)和题目代码
容器 (Container) 容器是一个隔离的沙箱,题目就在这个沙箱里运行

基础操作

  1. 镜像
  • docker images

    • 作用:查看本地所有镜像(看看你的武器库里都有啥)。
  • docker search <关键词>

    • 场景:你想找个现成的靶场,比如输入 docker search sqli-labs,可以直接在终端搜找别人做好的镜像。
  • docker pull <镜像名:标签>

    • 作用:把云端的镜像拉到本地。如果不写标签(冒号后面的部分),默认拉取 latest(最新版)。

    • 示例docker pull ubuntu:20.04

  • docker rmi <镜像ID或名字>

    • 作用:删除镜像(Remove Image)。

    • 避坑:如果报错说镜像正在被使用,说明有基于它的容器(即使是停止状态的)存在。必须先删容器,再删镜像。

  1. 容器
  • docker ps

    • 作用:查看正在运行的容器。这能帮你快速找到当前开了哪些端口。
  • docker ps -a

    • 作用:查看所有容器(包括报错闪退的、已经停止的)。排错时非常有用。
  • docker stop <容器名/ID> / docker start <容器名/ID>

    • 作用:优雅地停止/启动容器。
  • docker rm <容器名/ID>

    • 作用:删除容器。如果容器还在跑,可以加 -f (force) 强制删除:docker rm -f my_ctf_web
  • docker logs -f <容器名/ID>

    • 场景:Web 服务报错误了用这个命令实时追踪容器的后台输出日志,-f 表示持续跟踪不退出
  • docker exec -it <容器名/ID> /bin/bash

    • 作用:钻进运行中的容器里,获得一个交互式 Shell 进行调试
  1. 参数
参数 英文全称 说明 实用场景/示例
-d Detach 后台运行 跑 Web 靶场必加,避免终端被卡住无法执行其他命令
-p Publish 端口映射,格式:-p 宿主机端口:容器端口 示例:-p 8080:80(将宿主机8080端口映射到容器80端口)
–name Name 为容器自定义命名 示例:–name xiexie(避免系统生成随机难记的容器名)
-e Env 设置容器内的环境变量 示例:-e FLAG=flag{xiexie}
-v Volume 目录挂载,格式:-v /本地代码目录:/容器内部目录 宿主机改代码容器内实时生效,无需反复重启容器
–rm Remove 阅后即焚,容器停止后自动销毁 适合一次性测试,不残留容器文件、不占用磁盘空间
4. 核心命令大全
命令 功能 示例
docker run 启动一个新的容器并运行命令 docker run -d ubuntu
docker ps 列出当前正在运行的容器 docker ps
docker ps -a 列出所有容器(包括已停止的容器) docker ps -a
docker build 使用Dockerfile构建镜像 docker build -t my-image .
docker images 列出本地存储的所有镜像 docker images
docker pull 从 Docker 仓库拉取镜像 docker pull ubuntu
docker push 将镜像推送到 Docker 仓库 docker push my-image
docker exec 在运行的容器中执行命令 docker exec -it 23dfd567926a bash
docker stop 停止一个或多个容器 docker stop container_name
docker start 启动已经停止的容器 docker start container_name
docker restart 重启一个容器 docker restart container_name
docker rm 删除一个或多个容器 docker rm container_name
docker rmi 删除一个或多个镜像 docker rmi my-image
docker logs 查看容器的日志 docker logs container_name
docker inspect 获取容器或镜像的详细信息 docker inspect container_name
docker exec -it 进入容器的交互式终端 docker exec -it container_name /bin/bash
docker network ls 列出所有 Docker 网络 docker network ls
docker volume ls 列出所有 Docker 卷 docker volume ls
docker-compose up 启动多容器应用(从 docker-compose.yml 文件) docker-compose up
docker-compose down 停止并删除由 docker-compose 启动的容器、网络等 docker-compose down
docker info 显示 Docker 系统的详细信息 docker info
docker version 显示 Docker 客户端和守护进程的版本信息 docker version
docker stats 显示容器的实时资源使用情况 docker stats
docker login 登录 Docker 仓库 docker login
docker logout 登出 Docker 仓库 docker logout

如何让科学上网生效 / 配置镜像源

  1. 配置科学上网 这里略 走魔法代理即可
  2. 配置国内镜像源 这里在Docker Engine里面修改一下 换成国内镜像源 这里因为买服务器给了一个比较稳定的源 (打脸了 这个其实也不太稳定 最后还是走代理了)

Dockerfile

  1. Dockerfile 是一个文本文件,包含了一系列用于构建 Docker 镜像的指令 Docker 引擎通过读取这些指令来自动化构建镜像
  2. Dockerfile常用指令
指令 说明
FROM 设置构建镜像时使用的基础镜像
MAINTAINER 镜像的创建者
RUN 构建镜像时用于执行后面跟着的命令行命令(在 docker build 时执行)
CMD 类似于 RUN 指令,启动容器时用于执行后面跟着的命令行命令(在 docker run 时执行)
ENTRYPOINT 启动容器时会将其后面的命令当作参数,结合CMD 指令的命令一起执行
COPY 构建镜像时复制文件或者目录到容器里指定路径
ADD 功能与COPY指令类似,不同点在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令会自动复制并解压到 <目标路径>,在不解压的前提下,无法复制 tar 压缩文件(推荐使用 COPY指令)
ENV 设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量
ARG 构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量
VOLUME 定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷
EXPOSE 声明端口
WORKDIR 指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)
USER 用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)
LABEL 用来给镜像添加一些元数据(metadata),以键值对的形式
ONBUILD 用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令
下面是自己编写的一个最简单的php后门题的Dockerfile
1
2
3
4
5
FROM php:7.4-apache
WORKDIR /var/www/html/
COPY . .
RUN echo "flag{you_are_a_docker_master}" > /flag
EXPOSE 80

.dockerignore

当我们在 Dockerfile 中执行 COPY . /var/www/html/ 时,会把宿主机当前文件夹里的所有文件都塞进容器的 Web 目录 但我如果把自己的exp或者是什么源码之类的一起放上去 选手能直接访问到
.dockerignore作用就在这里 在 Dockerfile 同级目录下创建 .dockerignore 文件 在里面编写好需要忽略的文件 例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# .dockerignore 示例(注释用 # 开头) 
# 忽略 node_modules 目录(整目录)
node_modules/
# 忽略 logs 目录
logs/
# 忽略 .git 版本控制目录
.git/
# 忽略单个文件
.env
temp.txt
# 忽略所有 .log 后缀的文件
*.log
# 忽略所有以 temp 开头的文件/目录
temp*
# 例外:如果想忽略所有 .txt 但保留 important.txt
*.txt
!important.txt

这里还可以用到匹配 但是出题貌似用的少 现在能力还做不了那么复杂的题

docker-compose.yaml

Dockerfile只可以用来构建单个镜像,在docker-compose.yml文件中,可以定义多个服务,每个服务可以包含一系列配置选项,例如镜像名称、容器端口、环境变量等。
比如在一道题目里面 有前端 后端 加上一个服务之类的 需要手动配置它们相互通信的虚拟网络 还需要run好多次 如果配置文件写好之后 只需要 docker compose up -d 即可全部部署好
例如一个最简单的

builid构建镜像
ports去映射
假设有服务的那种 就比如是sql 文件结构如下

1
2
3
4
5
6
├── docker-compose.yaml
├── web/
│ ├── Dockerfile
│ └── index.php
└── db/
└── init.sql

文件

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
services:
# 服务 1:前端 Web
frontend:
# 注意!这里的 context 不是 . 了,而是 ./web,因为 Dockerfile 在 web 文件夹里
build:
context: ./web
dockerfile: Dockerfile
container_name: sqli_web
ports:
- "8080:80"
# 【重点】:依赖关系。保证数据库先启动,Web 后启动,防止连接失败
depends_on:
- backend_db

# 服务 2:后端 MySQL
backend_db:
# 数据库不需要我们自己写 Dockerfile 现场 build,直接用官方做好的镜像!
image: mysql:5.7
container_name: sqli_db
# 环境变量:官方 MySQL 镜像允许我们通过环境变量直接设置密码和库名
environment:
MYSQL_ROOT_PASSWORD: "toor"
MYSQL_DATABASE: "ctf_db"
# 端口映射:这里不写 ports
因为只有 frontend 容器需要连它。我们在宿主机上不需要连它。
# Compose 默认会让 frontend 和 backend_db 在同一个局域网内,前端只需连接 "backend_db:3306" 即可。

记第一次自己出题

出的题和寒假学习到的一个知识点相关 结合了sqlite的ATTTACH跨库查询 堆叠注入
目录如下

1
2
3
4
5
6
sqlite/
├── Dockerfile
├── docker-compose.yaml
└── src/
├── index.php
└── update.php
  1. 对dockerfile的要求 run一个sqlite 创目录 然后run两个数据库 一个是正常业务 一个是flag 对正常业务有读写 对flag数据库设权为444 还有就是Web 目录和 Flag 目录不可写 防止直接加上马RCE了
  2. 对前端 这里比较重要的是ai优化了非预期解 输出都经过 htmlspecialchars() 净化
  3. 对后端核心漏洞也就是直接拼接字符串 但是需要此库无任何有价值数据 需要跨库查询

    自己打打啦 具体文件就不放了