The moment you run docker run --privileged -v /:/host alpine:latest, you realize that 'secure' sandbox is just an illusion; the host OS is now your playground.
Hey everyone. Today, let’s look at this whole container security thing—not from the technical director's chair, but from the perspective of a Red Teamer who's both defending and trying to punch holes in those defenses on the field. As most of you know, a container isn't a Virtual Machine (VM). It's a set of processes sharing a kernel, restricted only by namespaces and cgroups. Yet, we often act like there are impenetrable walls around them. In the tests we run at testCompany, the biggest mistake we see is failing to realize exactly where that thin line ends.
Docker Socket: Putting the House Keys Under the Doormat
I often see the docker.sock file being mounted into containers in many CI/CD pipelines or monitoring tools. If an attacker manages to execute code in a container that has access to this socket, it's game over. Access to the Docker socket means full access to the Docker API.
Let’s look at a defanged scenario. What actually happens when an attacker runs this command from inside the container?
# An attacker with Docker socket access becoming root on the host
curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" \
-d '{"Image": "alpine", "Cmd": ["/bin/sh", "-c", "chroot /host /bin/sh"], "Binds": ["/:/host"]}' \
http://localhost/containers/create
This request tells the Docker daemon to create a new container but mount the host's root directory into itself. The result? A fully privileged shell on the host machine. If you're giving docker.sock to a container, you've basically accepted that the container is now the host itself. The fix is simple: Never share the socket. If you need to talk to the Docker API, do it behind a restricted proxy or use mTLS.
Privileged Containers and Capabilities
Then there's the infamous --privileged flag. My DevOps friend, when you flip this switch because 'the app is throwing errors, permissions aren't enough,' you've actually pulled the pin on a grenade. A privileged container gets almost all kernel capabilities on the host.
Linux Capabilities break down root privileges into pieces. For instance, CAP_NET_BIND_SERVICE only lets you open low ports, while CAP_SYS_ADMIN is like a Swiss Army knife; you can mount filesystems, change namespaces, and more.
In Red Team ops, our favorite things are over-privileged containers. For example, if a container only has the CAP_SYS_PTRACE capability, it can read the memory of other processes on the host and maybe dump an admin password from there.
What to do?
To shrink the attack surface, the 'Least Privilege' principle applies here too. Use securityContext in your Dockerfile or Kubernetes deployment:
# A secure example for Kubernetes
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsNonRoot: true
By dropping ALL capabilities and only adding what is strictly necessary, you turn a potential disaster into a minor incident. Stay safe out there, and remember: isolation is only as strong as your configuration.
