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