Over the years there has been a fundamental shift in evolving software development practices. In the past it was typical to build and maintain large monolithic code bases and run it on large servers, individual virtual machines, or even bare metal. Now, like many of us know already, many applications are being packaged as small services, loosely coupled together into what is called microservices architecture across a smaller group of distributed commodity hardware. The nature of this security infrastructure creates layers between application and host environments, facilitates fast and easy application of patches and updates across the technologies, and helps to maintain overall security compliance.
This past January there was a severe vulnerability disclosure affecting these containerized environments, which allows an attacker to escape from container to host system via docker-runc identified as CVE2019-5736. This vulnerability affects containerized technologies such as cri-o, containerd, and Kuerbenetes and it is to note an attacker would have to have root level access within the target container. Then an attacker would need to create a nefarious binary that is run on user entry. According to researcher’s attack description an attacker would then need their code execution to replace any dynamic library used by docker-runc with a custom .so file that has an additional global constructor. This function opens /proc/self/ exe for reading and then executes another binary which opens this time for writing to /proc/self/fd/3, which is a file descriptor of docker-runc which is opened before execve. An attacker could essentially subsequently write to the docker-runc file descriptor any arbitrary code they wish which would then overwrite the original docker-runc file on the system host and affect the host operating system.
As researchers describe the attack timeline, when a host user runs the affected container, the new docker-runc process is executed within the container but using the actual binary on the host file system. The docker-runc process however, loads the attacker controlled .so files from the container file system. The malicious global constructor function will be executed and load the attacker controlled binary. This binary overwrites the docker-runc on the host file system with a compromised docker-runc. Then when any user starts a docker image on the host file system the compromised docker-runc file is executed within the host environment, which fully compromises that system.
A fix to docker-runc was created, the applied code creates a memory based file descriptor, which loads a known good docker-runc binary. Before entering namespaces docker-runc is then run from the memory based file descriptor so the docker-runc on the host file system cannot be overridden. There also are other potential mitigations that involve appropriately configuring SELinux, configuring appropriate affected files to read-only, and lowering privileges of users inside of containers.
Sources
• https:// blog.dragonsector.pl/2019/02/cve2019-5736-escape-from-dockerand.html
• https://cyware.com/news/proof-ofconcept-for-container-escapevulnerability-unleashed-0c79f909