Copy Fail on Debian and SE Linux
I have just learned of the Copy Fail kernel vulnerability [1] thanks to alexanderkjall@mastodon.social (who I have just followed on Mastodon and I recommend that you follow too). The question for me (after installing the patched kernel the systems of mine that are most exposed) is whether SE Linux would have stopped that.
Basic Policy Analysis
For the SE Linux policy analysis the alg_socket class is the one that is related to the exploit. So the following policy analysis command (run as non-root with policy copied to /tmp from a running system) shows what domains are allowed access on my current Debian development system:
$ sesearch -A -c alg_socket /tmp/policy.35
allow NetworkManager_t NetworkManager_t:alg_socket { accept bind create read setopt write };
allow bluetooth_t bluetooth_t:alg_socket { accept append bind connect create getattr getopt ioctl listen read setattr setopt shutdown write };
allow daemon init_t:alg_socket { getattr getopt ioctl read setopt write };
allow devicekit_disk_t domain:alg_socket getattr;
allow lvm_t lvm_t:alg_socket { append bind connect create getattr getopt ioctl read setattr setopt shutdown write };
allow sosreport_t domain:alg_socket getattr;
allow sysadm_t domain:alg_socket getattr;
allow unconfined_domain_type domain:alg_socket { accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom relabelfrom relabelto sendto setattr setopt shutdown write };
The above is the same as on the Trixie release policy as these things aren’t changed often. Below is from Debian/Bookworm which is the same apart from Bookworm not allowing lvm_t:
$ sesearch -A -c alg_socket /tmp/policy.33
allow NetworkManager_t NetworkManager_t:alg_socket { accept bind create read setopt write };
allow bluetooth_t bluetooth_t:alg_socket { accept append bind connect create getattr getopt ioctl listen read setattr setopt shutdown write };
allow daemon init_t:alg_socket { getattr getopt ioctl read setopt write };
allow devicekit_disk_t domain:alg_socket getattr;
allow sosreport_t domain:alg_socket getattr;
allow sysadm_t domain:alg_socket getattr;
allow unconfined_domain_type domain:alg_socket { accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom relabelfrom relabelto sendto setattr setopt shutdown write };
I checked every Debian policy back to when the alg_socket class was first added and found that the older versions had fewer domains granted access. The most recently added was bluetooth_t and the one before that was NetworkManager_t.
The Risky Lines
Of those allow statements the following are the risks:
Unconfined Domains and the unconfined_domain_type Attribute
When writing policy lines like the following line aren’t generally considered a problem as unconfined domains are allowed full access to the system. However it can be an issue if you have a process in an unconfined domain without root access, which means a regular user login. Unfortunately this happens to be where this exploit and the default Debian SE Linux configuration intersect.
allow unconfined_domain_type domain:alg_socket { accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom relabelfrom relabelto sendto setattr setopt shutdown write };
The following shell code gets a list of unconfined domains which can be entered from user domains.
A="" for n in $(seinfo -x -a unconfined_domain_type|grep _t$) ; do A="$A|($n)" done A=$(echo $A|sed -e s/^.//) sesearch -T -s user_application_exec_domain -c process|egrep "$A;"
Below is the output on a Debian/Trixie (Stable) system. So a confined user in the user_t domain could run an X server and try and get it to run the exploit code (which seems difficult) or running a Wine or Mono program from the Window manager in a Wayland environment.
type_transition user_t xserver_exec_t:process xserver_t; type_transition user_wm_t mono_exec_t:process mono_t; type_transition user_wm_t wine_exec_t:process wine_t; type_transition user_wm_t xserver_exec_t:process xserver_t;
The issue of unconfined domains in SE Linux policy needs much more work. I’ll write some blog posts about it later and the next release of Debian will be significantly better in this regard.
Daemons that Have Access
allow NetworkManager_t NetworkManager_t:alg_socket { accept bind create read setopt write };
allow bluetooth_t bluetooth_t:alg_socket { accept append bind connect create getattr getopt ioctl listen read setattr setopt shutdown write };
Network Manager is something that can potentially be exploited by a desktop user as it has a large attack surface for the desktop interface. But as the vast majority of desktop user accounts are unconfined that’s not an issue. This might be an issue for some restricted desktop PCs, maybe kiosk systems and those PCs that were being installed in prisons.
The bluetooth_t domain is used by the bluetooth daemon that runs as root. While we generally are less concerned about a root process being exploited the daemon will handle some data from hostile sources and it could be used as an escalation attack by someone with a hostile Bluetooth device.
These can’t be exploited without another bug.
The Lines that Aren’t Problems
The getattr Lines
allow devicekit_disk_t domain:alg_socket getattr; allow sosreport_t domain:alg_socket getattr; allow sysadm_t domain:alg_socket getattr;
The above getattr access isn’t an issue as it just allows seeing process information, and it’s also by privileged domains.
The init_t Sockets
allow daemon init_t:alg_socket { getattr getopt ioctl read setopt write };
The daemon access to sockets inherited from init_t probably isn’t a great idea, it’s from the following section in init.te which is to allow socket activation for daemons, the comment is concerning in this context. Also socket_class_set is overly broad as without even inspecting the systemd source code I’m pretty sure that far fewer than 1/3 of the 55 classes allowed by that rule are actually supported in systemd.
ifdef(`init_systemd',`
# Until systemd is fixed
allow daemon init_t:socket_class_set { getattr getopt ioctl read setopt write };
But that’s not really a problem as systemd has to just not create a socket of that type, if a hostile party can make systemd create such sockets then you have probably already lost.
SE Linux Protection
Overall SE Linux systems running confined users (kiosks and other confined GUI environments) will be protected barring a bug in Network Manager or the Bluetooth daemon as long as there is no Xserver installed (or the X server won’t run scripts on startup), no Wine system installed, and no Mono.
SE Linux servers and VMs will be protected against daemon issues as long as the daemon isn’t unconfined.
To convert the default login to user_t run the following commands:
semanage login -m -s user_u -r s0 __default__ restorecon -R -v -F /home
But it is still possible to access an unconfined domain from user_t (a topic I will address in detail in a future blog post).
To remove unconfined entirely (not a task for novices or something to be done on in production without testing and planning) run the following commands:
semanage login -m -s root -r s0 root # logout and login again semodule -X 100 -r unconfined
Then a Debian/Trixie system running SE Linux will be safe against this attack even when running a vulnerable kernel.
If you still want to use root as unconfined_t but still have untrusted shell users then run the following command to remove the easiest ways for users to run a program in an unconfined domain:
semodule -X 100 -r mono wine
Success and Failure
Blocked by SE Linux
Below is what happens on stdout/stderr when SE Linux blocks the exploit (tested with vulnerable Debian kernel 6.12.74+deb13+1-amd64):
test@testing1:~$ python3 ./copy_fail_exp.py
Traceback (most recent call last):
File "/home/test/./copy_fail_exp.py", line 9, in <module>
while i<len(e):c(f,i,e[i:i+4]);i+=4
~^^^^^^^^^^^^^^
File "/home/test/./copy_fail_exp.py", line 5, in c
a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o)
File "/usr/lib/python3.13/socket.py", line 233, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^PermissionError: [Errno 13] Permission denied
test@testing1:~$ su
Password:
When the attack is blocked by SE Linux there will be no messages in the kernel message log but the SE Linux audit log (typically stored in /var/log/audit/audit.log) will have lines like the following:
type=AVC msg=audit(1777803068.070:76): avc: denied { create } for pid=811 comm="python3" scontext=user_u:user_r:user_t:s0 tcontext=user_u:user_r:user_t:s0 tclass=alg_socket permissive=0
type=SYSCALL msg=audit(1777803068.070:76): arch=c000003e syscall=41 success=no exit=-13 a0=26 a1=80005 a2=0 a3=0 items=0 ppid=791 pid=811 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts0 ses=1 comm="python3" exe="/usr/bin/python3.13" subj=user_u:user_r:user_t:s0 key=(null)ARCH=x86_64 SYSCALL=socket AUID="test" UID="test" GID="test" EUID="test" SUID="test" FSUID="test" EGID="test" SGID="test" FSGID="test"
type=PROCTITLE msg=audit(1777803068.070:76): proctitle=707974686F6E33002E2F636F70795F6661696C5F6578702E7079
For that the :76 is the audit log entry number, the command “ausearch -i -a 76” will interpret that message with the following output:
type=PROCTITLE msg=audit(05/03/26 10:11:08.070:76) : proctitle=python3 ./copy_fail_exp.py
type=SYSCALL msg=audit(05/03/26 10:11:08.070:76) : arch=x86_64 syscall=socket success=no exit=EACCES(Permission denied) a0=alg a1=SOCK_SEQPACKET a2=ip a3=0x0 items=0 ppid=791 pid=811 auid=test uid=test gid=test euid=test suid=test fsuid=test egid=test sgid=test fsgid=test tty=pts0 ses=1 comm=python3 exe=/usr/bin/python3.13 subj=user_u:user_r:user_t:s0 key=(null)
type=AVC msg=audit(05/03/26 10:11:08.070:76) : avc: denied { create } for pid=811 comm=python3 scontext=user_u:user_r:user_t:s0 tcontext=user_u:user_r:user_t:s0 tclass=alg_socket permissive=0
When it Works
Below is what happens when it works (again tested with Debian kernel 6.12.74+deb13+1-amd64):
test@testing1:~$ python3 ./copy_fail_exp.py #
Here is the kernel log when the attack works:
[ 30.441830] alg: No test for authencesn(hmac(sha256),cbc(aes)) (authencesn(hmac(sha256-avx2),cbc-aes-aesni)) [ 30.447466] process 'su' launched '/bin/sh' with NULL argv: empty string added
When the Kernel Isn’t Vulnerable
If the kernel isn’t vulnerable and SE Linux permits the attack (EG run from an unconfined domain) the following is seen on stdout/stderr:
$ python3 ./copy_fail_exp.py Password: su: Authentication failure
In that situation the kernel will log something like the following:
[ 36.647023] alg: No test for authencesn(hmac(sha256),cbc(aes)) (authencesn(hmac-sha256-lib,cbc-aes-aesni))
This was tested on the Debian/Unstable kernel 6.19.13+deb14-amd64.
Conclusion
Run the following commands and then force all users to logout to make a Debian SE Linux system offering shell access reasonably safe against this bug. But also upgrade your kernel as soon as convenient because having multiple layers of protection is always good.
semanage login -m -s user_u -r s0 __default__ restorecon -R -v -F /home semodule -X 100 -r mono wine
The GrapheneOS people are doing really good work on securing phones, I am most interested in Mobian (Debian on phones) but for people who have made different choices GrapheneOS is a good option. Here is the GrapheneOS statement on Copy Fail (they are not vulnerable to it) [3]. For people interested in running a secure Android build GrapheneOS is the best option. Their supported devices list shows Pixel 6 to Pixel 10 supported and Pixel 8 to Pixel 10a recommended [4]. In Australia Kogan sells refurbished Pixel 6 phones starting at $251 including delivery and refurbished Pixel 8 phones starting at $499 with “First” membership, they seem to have the cheapest Pixel phones.
I want to make Debian more like Android in terms of security, but that’s a topic for other blog posts.
04 May, 2026 06:26AM by etbe
























