Insights & Research Blog

My exploit demo of COPY FAIL (CVE-2026-31431): Proof that containers aren't a universal shield

My exploit demo of COPY FAIL (CVE-2026-31431): Proof that containers aren't a universal shield

0:00
/1:19

A common misconception in the industry is that containerization inherently provides an impenetrable security boundary. Organizations often fall into the trap of assuming that deploying applications in containers is a complete security solution in itself. While CVE-2026-31431 is a vulnerability within the Linux kernel and not the container runtime itself, it perfectly illustrates the fragility of container isolation. It serves as a stark reminder that containers fundamentally rely on kernel trust; once the kernel is compromised, the entire security model collapses.

💡
Disclaimer: This material is for educational and security research purposes only. The author is not responsible for any misuse or damage caused by the information provided. Any techniques or tools described must only be used in controlled environments or with the explicit permission of the target system owner. Any provided code is supplied 'AS IS' without warranty of any kind. Use at your own risk.

What is happening in this demo?

To help you understand how this exploit bypasses isolation, here is a step-by-step breakdown. The most important thing to note is that we are looking at two entirely separate docker containers sharing the same host kernel:

The Setup: I have two terminal sessions. On the left is Container A, which has been compromised (running as the low-privileged user www-data). On the right is Container B, a separate, isolated environment where a root user is active. This demo assumes the attacker has already gained access to Container A (e.g., via a web shell), as triggering the memory poisoning requires local execution privileges.

Initial Verification: The root user in Container B checks for the existence of a file named /root/hacked.txt. The file does not exist, and Container B appears completely secure and isolated from Container A.

The Poisoning (Container A): The low-privilege user in Container A runs a custom exploit targeting the /usr/bin/ls binary. Instead of trying to modify the file on disk, it exploits a flaw in the kernel's Page Cache.

Breaking Isolation: Because both containers run on the same kernel, they share the same Page Cache for common system binaries. My exploit "poisons" the memory pages of ls globally. The kernel now serves this malicious version of the command to every docker container on the host, including the "secure" Container B.

The Payload (Container B): When the root user in Container B simply tries to list files using ls, they unknowingly execute the malicious code injected from Container A.

The Result: The "poisoned" ls executes with root privileges inside Container B, successfully creating the /root/hacked.txt file. This demonstrates a successful cross-container attack where a low-privilege process in one container forces a root user in another container to execute arbitrary code.

How the "COPY FAIL" Vulnerability Works

To understand why this exploit is so devastating, we have to look at how the Linux kernel optimizes memory through the Page Cache. To save resources, the kernel keeps frequently accessed file data (like the code for the ls command) in RAM. When multiple containers run the same binary, the kernel maps those same physical memory pages to every container.

Under normal circumstances, if a user tries to modify one of these shared pages, a security mechanism called Copy-on-Write (COW) kicks in: the kernel creates a private, modified copy of the page for that user while keeping the "global" version safe for other containers.

CVE-2026-31431 effectively breaks this protection. Due to a flaw in how the kernel tracks page "dirtiness" during specific memory-mapping sequences, my exploit is able to bypass the COW mechanism entirely. Instead of the kernel creating a private copy for Container A, it allows the www-data user to write directly into the global Page Cache.

By targeting the memory pages of /usr/bin/ls, the exploit "poisons" the binary in RAM. Since the Page Cache is shared across the entire host—regardless of container boundaries—the next time the root user in Container B runs ls, the kernel serves them the malicious code. This results in an immediate, silent execution of arbitrary code with root privileges, proving that when the kernel's memory management fails, container isolation becomes an illusion.

Who is affected?

The situation is truly extreme. Reports indicate that nearly all Linux kernels released between 2017 and 2026 are likely vulnerable. I have personally verified this on several different systems, and the consistency of the exploit is staggering.

For the most up-to-date and detailed list of affected versions, please refer to the official vulnerability page:

👉 https://copy.fail/#affected

Looking Ahead: What’s the Lesson?

The inevitable post-incident post-mortems often focus on the unpredictability of zero-day exploits. However, the true lesson of CVE-2026-31431 (COPY FAIL) lies not in the flaw itself, but in the over-reliance on a single layer of abstraction.

While hardening measures are essential, this vulnerability underscores several critical principles for modern infrastructure security:

  1. The Limits of Shared-Kernel Isolation: Containerization is a tool for efficiency, not an absolute security barrier. Because standard containers share the host's kernel, they carry an inherent risk in multi-tenant or high-stakes environments. For critical systems and sensitive data, organizations must consider hardware-assisted isolation or sandboxed runtimes (such as Kata Containers or gVisor). By leveraging hardware-backed boundaries, these technologies ensure that even a total kernel compromise remains confined to a single, isolated environment rather than threatening the entire host.
  2. Defense in Depth is Mandatory: Relying solely on the container boundary is a high-stakes gamble. A robust security posture must include proactive kernel hardening and runtime protection. Implementing restrictive Seccomp profiles and AppArmor/SELinux policies can significantly reduce the attack surface, even when a kernel-level vulnerability exists.
  3. Minimizing the Patching Window: When a vulnerability affects the underlying kernel, every hour of delay increases the risk of a full-scale breach. This exploit serves as a reminder that organizations must find ways to shrink their patching cycles.
Note on the PoC: The exploit shown in this demo is my own customized version of the publicly available proof-of-concept published by tgies. I adapted his work to better demonstrate the cross-container implications of this vulnerability. Original source: https://github.com/tgies/copy-fail-c