Skip to content
Tech News
← Back to articles

CVE-2026-31431: Copy Fail vs. rootless containers

read original more articles
Why This Matters

The CVE-2026-31431 vulnerability highlights the importance of understanding exploit mechanics and kernel behavior in container security. Its analysis underscores the need for robust isolation in rootless containers to prevent privilege escalation, which is critical for safeguarding both industry infrastructure and consumer data.

Key Takeaways

Table of Contents

Introduction

In the previous post about SELinux MCS and GitLab runners, I briefly mentioned CVE-2026-31431 (“Copy Fail”) as a motivating example for per-job VM isolation. After that post went out I spent the weekend setting up a lab to actually run the exploit, trace it at the syscall level, and verify that the rootless Podman architecture we deploy on GNOME’s runners would contain it. This post documents the entire process: from disassembling the shellcode to watching the kernel reject the privilege escalation in real time.

The vulnerability

For a full technical breakdown of the root cause, the scatterlist mechanics, and the disclosure timeline, read Theori’s excellent writeup at xint.io/blog/copy-fail-linux-distributions. In this blog post we’ll initially analyze the shellcode embedded in the public exploit, then set up a lab to run it inside a rootless container and subsequently trace what happens at the kernel level.

Analyzing the shellcode

In the days following the disclosure I noticed a lot of people running the exploit on their systems without bothering to check what the shellcode actually does. Executing a compressed binary blob from a GitHub repository you have never audited is not a great security practice — for all you know it could be exfiltrating data or dropping a backdoor alongside the privilege escalation. So before running anything, let’s look at what the actual shellcode contains.

The shellcode is embedded in the Python exploit as a compressed and hex-encoded string:

78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a 154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56 c3ff593611fcacfa499979fac5190c0c0c0032c310d3

The script uses zlib.decompress() to turn this into raw bytes. To extract and inspect the payload:

... continue reading