The reality of container escapes
Here’s a transcript of the podcast for your convenience.
Hello, my name is Brandon Edwards. I am co-founder and Chief Scientist at Capsule8. Today on Help Net Security, I’ll be talking about container escapes.
Recently there was a container escape demonstrated through this RunC vulnerability, which was interesting mostly in the public reaction. The reason for that is that there seem to be a sense of shock around it like “oh my goodness, this is possible, this can happen!” Of course, a vulnerability in the container management software is going to be of interest and could lead to this sort of scenario.
But the amount of shock and awe that we saw coming out of this, seemed to demonstrate that it’s maybe misunderstood or not properly perceived how easy it is to escape from a container, given other vulnerabilities, specifically in the kernel. The rate at which bugs in the kernel are discovered and patched, it’s frequent enough that, once you understand how that can be used to escape a container, this should just be treated along the same lines.
Containers are after all just a means of organizing processes and any vulnerability in the kernel, which you can exploit to elevate the privileges of a process, for example, could also be exploited to change the namespace information, file system mount location, capabilities granted by that process, assuming that you’ve exploited that vulnerability to gain arbitrary code execution. This was interesting to us internally for understanding the sort of limits and bounds of containers, and how we would detect someone escaping this, have developed exploits for breaking out of containers prior to RunC vulnerability coming out.
I think it’s important to talk about and understand the simplicity involved: that for a given kernel exploit there are maybe several hundred lines, the payload to get out of the container is 10 of C code. This is because you already have to go through all this complexity with most kernel exploits when getting arbitrary code execution. You have to trigger the bug, which is going to somehow give you either a right primitive to corrupt some sort of function pointer or something on the stack.
Once you gain control of execution, you then have to pivot to some sort of gadget, ROP gadget, which will disable certain kernel mechanisms. From there you can then return into userland code, operating as the kernel. Once you’re at that point, you’ve done all the hard work, relatively speaking.
Taking a kernel exploit that does a lot of hard work and sets its privileges to root, it’s not much harder to take that same exploit and, while you’re in there playing with these structures, also update where your file system is and the capabilities that you have.
I really want to talk about that because, if container escapes are a concern for people, as we’ve seen with the RunC vulnerability, their concern should be more heavily focused on general kernel hygiene and security than necessarily Docker specific one-off vulnerabilities, or something in the RunC code base. Those are obviously still of interest, but it should be implied that with any kernel vulnerability you’ve lost any isolation that you assumed to be there from a security sense, with containers.
Isolation is not security property of containers. That’s not to say containers don’t have security properties offered by how it isolates the software, organizationally speaking. I generally want to promote container use as I think it leads to better application development processes. I think it does have some security properties in the reduction of attack surface. The clear expression of intent. You know what the container should be doing based on what it is packaged to do.
It removes a lot of the issues with dependencies and obviously was there to ship software in the first place, and anytime you can ship software in a cleaner way, you have security benefits. I just fear that there was too much trust placed in this magical mechanism of isolation, that it also brought security with it. It’s important to recognize that there’s always going to be other vulnerabilities. They don’t have to affect the container runtime. There will be ways to escape it, it’s just we haven’t seen it much exploited in the wild.
To help people further understand the implications of this and how easy it is from the position of executing arbitrary code from the kernel to escape a container, we’ve recently published a post demonstrating this with a walkthrough of the structures involved, and the code required to basically take a kernel of vulnerability that has already been exploited for arbitrary code execution, and walk out of the container by updating structures. For more information please visit our post on the Capsule8 blog.