CVE-2020-15275 in containerd-shim API

CVE-2020-15275: New Vulnerability Exploits containerd-shim API

A year of challenges isn’t quite over yet, as a new vulnerability was found in containerd, CVE-2020-15257. When exploited, after providing a connection through the container to the host network, an attacker can gain root privileges on the host. This vulnerability was disclosed by Jeff Dileo of NCC Group, our investigation by Team Nautilus is aligned with his findings.

Containerd is the industry-standard container runtime underlying Docker and Kubernetes, available as a daemon for Linux. It can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage, network attachments, etc. Containerd-shim is a child process of containerd that serves a single container and takes care of the container lifecycle and exposes its functions to containerd through containerd-shim API.

This API is exposed over an abstract namespace Unix domain socket that is accessible from the root network namespace. Because of this, containers with host networking and UID 0 can access this API and cause containerd-shim to perform malicious actions such as running a privileged container, enabling escalation, and establish root privileges on the host.

Running containers in the host network namespace is done by:

  • Docker: docker run --net=host
  • Kubernetes: .spec.hostNetwork: true

Getting into the details

The use of abstract namespace Unix domain socket was the main issue leading to this vulnerability. Normal Unix domain sockets are bound to a file path and have permissions as any file would have, but these abstract Unix sockets are bound to network namespace and are “permissonless.” You can easily spot them in the command line when running the netstat command as they all begin with a null byte represented with an @ sign.

CVE-2020-15275 in containerd-shim API

We can clearly see some Unix domain sockets attached to a path on the filesystem, but we also see the abstract namespace Unix socket in the form of @/containerd-shim/<id>.sock@ (one for each docker container running on this system). As we mentioned, these sockets are permissionless. This means they have no built-in access control, so any authorization or authentication needs to be taken care of by the process of listening on these sockets. Unfortunately, the only check performed is that the user connecting is the same as the one running containerd-shim.

Exploitation

So, given that we can communicate with containerd-shim API, what does this API provide? By looking at the containerd-shim API protobuf, we can find out:

CVE-2020-15275 in containerd-shim API

This shows that there’s a lot of things we can do, but the Create() and Start() stand out, these APIs match the ‘docker create’ and ‘docker run’ functions, and should be more than enough to take over a host.
All we need to do is provide the API with the desired privileged container config so we can escalate our grip and take over the host from there. As with a direct socket we control the configuration completely, we can add any capabilities in any PID namespace we want, disable AppArmor/Seccomp, and more.

Solutions

To check if you are vulnerable, use the command below that will determine if a vulnerable containerd-shim process is running:

‘cat /proc/net/unix | grep 'containerd-shim' | grep '@'’

To protect yourself, here are several actions you can take:

1. Update to the newest version of containerd.

The CVE was fixed in containerd v1.4.3/v1.3.9 by switching away from abstract namespace sockets to file-based UNIX sockets.

2. Run containers as non-root/UID!=0 which will block access to the abstract namespace socket.

Docker:
docker run --net=host --user 11111 --security-opt no-new-privileges

Make sure the user-specified does not match the UID of containerd, no-new-privileges are used to prevent the use of sudo.

Kubernetes:
In .spec.containers.securityContext, specify:

runAsUser: 11111
allowPrivilegeEscalation: false

3. Use AppArmor to deny access to all abstract sockets by adding this line to your AppArmor profile:

deny unix addr=@**,

Docker:
docker run --net=host --security-opt apparmor=<profile-name>

After fixing the issue, it’s important to remember that running containers in the host network namespace is generally considered insecure — it’s a good practice to avoid using them altogether. However, if you need to use them, be sure to apply restrictions described above.

Conclusion

CVE-2020-15257 is another good example of how a restricted container with just one extra privilege can lead to a complete privilege escalation and host takeover. If attackers can successfully connect to a containerd-shim API socket, they can directly compromise a host.

As new vulnerabilities will keep popping up, it’s important not only to secure your boundaries, but to restrict, monitor, and secure the inside of your cloud infrastructure as well.

Picture of Gal Singer

Gal Singer

Gal is a Security Researcher at Aqua. As part of the Aqua research team, his work focuses on researching vulnerabilities in Kubernetes and Networking around the cloud native world. When not at work, he likes going to music concerts and spending time at the beach with his friends.

Security Threats, Container Security