Blog

19 Years in the Shadows: CIFSwitch Hands Out Root Through the SMB Mount Mechanism.

CIFSwitch
CVE / Linux / Security

19 Years in the Shadows: CIFSwitch Hands Out Root Through the SMB Mount Mechanism.

Picture this: a regular unprivileged user is running on your server — a developer, a VPS tenant, a student on shared hosting. They run a single command. A second later they have root. SELinux is in enforcing mode. AppArmor is active. Neither of them did anything.

That’s CIFSwitch — a Linux kernel vulnerability discovered and disclosed by researcher Asim Viladi Oglu Manizada on May 28, 2026. No CVE has been assigned at the time of publication. The kernel patch has been ready and queued for stable branches for over a week. The public proof-of-concept dropped alongside the disclosure — meaning anyone can reproduce the attack right now.

The bug has been sitting in the code since 2007. Nineteen years. It survived countless audits and thousands of updates, completely unnoticed — because it’s not a buffer overflow or a use-after-free. It’s a logic error at the boundary between the kernel and userspace, and spotting it required looking at the whole system at once, not at individual components.

WHAT IS CIFS AND cifs.upcall

CIFS (Common Internet File System) is the Linux kernel’s implementation of the SMB protocol — the thing that lets Linux mount network shares from Windows servers using mount -t cifs. The cifs kernel module handles the actual filesystem layer: mounting, reading, writing, negotiating with the server. But there’s a catch — the kernel doesn’t handle Kerberos authentication itself. That’s delegated through a separate mechanism.

When the CIFS client needs a Kerberos/SPNEGO token for an authenticated mount, it uses the Linux keyring subsystem: it requests a key of type cifs.spnego. The system sees the request and, according to the rule in /etc/request-key.d/cifs.spnego.conf, launches a userspace helper — cifs.upcall — as root. The helper fetches the Kerberos material and returns it to the kernel. All of this is part of the cifs-utils package.

The key point here: cifs.upcall runs as root. That’s not a bug in itself — it’s an architectural decision, because the helper needs privileges to work with credential caches belonging to different users. The problem is that the kernel never checked who actually asked for that helper to be launched.

HOW THE BUG WORKS

When the kernel requests a cifs.spnego key, it builds a description string from real system state: host, uid, creduid, the process pid, and the upcall target type. Something like: ver=0x2;host=fs.acme.com;ip4=192.168.1.10;sec=krb5;uid=0x3e8;creduid=0x3e8;pid=0x4f2a;upcall_target=app. These fields are genuine — the kernel pulls them from its own state.

The problem is in the definition of the cifs_spnego_key_type key type in fs/smb/client/cifs_spnego.c. Before the patch, the struct looked like this:

struct key_type cifs_spnego_key_type = {
    .name        = "cifs.spnego",
    .instantiate = cifs_spnego_key_instantiate,
    .destroy     = cifs_spnego_key_destroy,
    .describe    = user_describe,
};

Notice what’s missing? The .vet_description field — the hook that’s supposed to validate the legitimacy of the key description. Without it, the kernel couldn’t tell the difference between a request from the CIFS client and a request from any other process. An ordinary unprivileged user could call request_key("cifs.spnego", completely_fake_description, "") — and the system would respond exactly as it would to a kernel request: by launching cifs.upcall as root.

One important detail: the kernel didn’t even need to successfully return the key. cifs.upcall launched before the kernel issued the -ENOKEY error. The attack succeeded before the refusal arrived.

HOW IT IS EXPLOITED

The attacker crafts a fake description string, setting upcall_target=app and supplying their own process pid. From there, the chain of trust starts working against the system.

cifs.upcall, now running as root, parses the description and sees upcall_target=app. In the code, that’s a signal: “switch into the application’s namespace to work correctly inside containers.” The helper calls switch_to_process_ns(arg->pid) and moves into the namespace of the process the attacker specified — a namespace the attacker controls entirely.

Now a root process is inside the attacker’s namespace. The next step: getpwuid(uid) — the helper looks up the account through NSS (Name Service Switch) to get the user’s gid. NSS reads its configuration from /etc/nsswitch.conf, which the attacker has already replaced inside their namespace. The fake nsswitch.conf points to libnss_pwn.so.2 — the attacker’s library. The root helper loads it. The library executes as root.

In Manizada’s PoC, that library writes a rule to /etc/sudoers.d — giving the attacker the ability to run any command as root via sudo.

WHAT HAPPENS NEXT — DEPENDS ON SYSTEM CONFIGURATION

CIFSwitch isn’t a universal vulnerability. Successful exploitation requires four conditions to hold simultaneously: a vulnerable kernel (the code path has been there since 2007), cifs-utils version 6.14 or higher with the cifs.spnego request-key rule, unprivileged user namespaces permitted, and an LSM policy that doesn’t block the chain.

Here’s where it gets interesting. SELinux in enforcing mode sounds like solid protection. But testing shows: CentOS Stream 9 GNOME, Rocky Linux 9 Workstation, and AlmaLinux 9.7 are all exploitable with SELinux enforcing active. AppArmor doesn’t help on Linux Mint 21.3/22.3 or SLES 15 SP7 either. The default policies simply don’t cover this attack vector.

Only stricter configurations hold up: Fedora 40–44, CentOS Stream 10, and Rocky Linux 10 aren’t exploitable — but only because their SELinux is configured more tightly. Run setenforce 0 and the protection disappears. Ubuntu 26.04 blocks the attack through its AppArmor userns policy. On 24.04 the policy is also active, but there’s a bypass: aa-exec -p trinity launches a process under an AppArmor profile that permits user namespace creation — and the attack works again. Amazon Linux 2 is unaffected, but only because it ships cifs-utils 6.2, which is too old to have the namespace-switching code path.

REAL-WORLD ATTACK CHAIN

Scenario: a VPS tenant on Ubuntu 22.04 with cifs-utils installed. The server is part of a shared infrastructure — multiple clients, each with their own unprivileged user. One tenant downloads the PoC from GitHub and runs a single command. A second later they have root access to the entire server — including every other tenant’s data, databases, SSL certificates, and SSH keys.

No root password needed. No network access. No memory exploitation — no heap spray, no ROP chains. Just one request_key() syscall with a fake description, and the system’s own logic does the rest.

The initial access vector can be anything: SSH access with limited privileges, a web shell through a vulnerable web application, a compromised service account, a container escape from a container that doesn’t isolate user namespaces. All that matters is an unprivileged user on a vulnerable system.

TIMELINE

2007 — the bug enters the codebase. cifs_spnego_key_type is defined in fs/smb/client/cifs_spnego.c without a .vet_description hook. Not an implementation mistake — a missing check in the design of the mechanism.

May 16, 2026 — Asim Viladi Oglu Manizada reports the issue to the kernel and cifs-utils maintainers.

May 16–27, 2026 — the kernel patch (commit 3da1fdf4efbc) is prepared and queued for stable branches. A coordinated embargo runs with linux-distros@ to give distributions time to prepare packages before public disclosure.

May 28, 2026 — the embargo expires. Manizada publishes the full writeup on heyitsas.im and the PoC on GitHub, simultaneously with the oss-security mailing list disclosure. The kernel patch has been in the stable queue for over a week at this point, but distribution packages are still rolling out.

June 1, 2026 — patches continue to arrive for Red Hat, Ubuntu, Debian, SUSE, Oracle, and Amazon Linux. No CVE identifier has been assigned yet.

HOW DID THIS SURVIVE 19 YEARS

A developer’s mental model can’t hold the entire system’s trust graph at once. When the CIFS upcall mechanism was implemented in 2007, the developer saw their piece of the puzzle: the kernel requests a key, the userspace helper receives it and does the work. The logic is correct. What wasn’t obvious was that any process could initiate the same request with fake data, because the key type doesn’t verify the origin of the description.

This is a classic confusion attack pattern: component A trusts data that it assumes can only come from component B, but there’s no mechanism that actually guarantees that. Finding this kind of vulnerability in a manual audit is extremely difficult — you need to hold the kernel code, the userspace helper, the keyring mechanism, and how NSS behaves in non-standard namespace conditions all in your head simultaneously. No auditor looks at that entire graph at once.

The exploitation conditions are also non-trivial: you need a specific version of cifs-utils with namespace-switching support, and an LSM policy that doesn’t block the chain. On servers without cifs-utils the bug isn’t exploitable at all. That sharply reduced the chance of anyone stumbling across it in normal operation.

WHY ARE THEY FINDING IT NOW

CIFSwitch wasn’t found by a human — or rather, it was found by a human with a tool that let them see exactly this type of cross-component relationship.

Manizada describes the approach in his writeup: he equipped LLM agents with a graph traversal tool inspired by the GraphWalk research. The agents built a semantic graph of security-relevant objects — who creates an object, who consumes it as authoritative, where the gap opens between what an object claims about itself and what its consumer assumes. On a large enough graph, models with this tool outperformed reasoning models without it.

This is an important signal for the whole industry: the “confusion across trust boundaries” class of vulnerabilities is exactly what LLM-assisted analysis finds better than humans. A human auditor reads code linearly; a graph traverser sees connections across dozens of files and components. CIFSwitch won’t be the last bug of this type found this way.

WHY IT MATTERS

The scope is striking. Manizada tested around 30 distribution and edition combinations. Exploitable in default configuration, out of the box: Linux Mint 21.3 and 22.3, CentOS Stream 9 GNOME, Rocky Linux 9 Workstation, every version of headless Kali Linux from 2021.4 through 2026.1, AlmaLinux 9.7, and SLES 15 SP7. These aren’t obscure distros — they’re mainstream systems running on production servers worldwide.

Cloud images are a separate story. AlmaLinux 9.7 Azure cloud image is exploitable out of the box — cifs-utils comes preinstalled. Amazon Linux 2023 is exploitable under SELinux permissive, but only if cifs-utils has been manually installed — it’s not there by default. That means a portion of cloud instances running these images may be vulnerable right now, if the administrator hasn’t updated the kernel yet.

For anyone managing shared hosting or a VPS platform, the situation is particularly acute: one compromised tenant with shell access means full control over the host and every other tenant’s data. In enterprise environments with many developers or service accounts, any of them is a potential privilege escalation vector to root.

HOW TO PREVENT IT HAPPENING AGAIN

CIFSwitch isn’t an isolated case. CopyFail (CVE-2026-31431) used the same principle months earlier: a privileged component trusted data whose origin was never verified. The “confusion across trust boundaries” attack class — logic bugs at the interface between components with different privilege levels — comes up regularly and is extremely difficult to catch in a manual audit.

The architectural answer is the principle of least trust: a privileged component must not accept data from a less privileged one without explicit verification of origin. In the kernel, that means .vet_description in the key type. In userspace helpers, it means not treating key description fields as kernel-originated without a verification step.

At the server configuration level: unprivileged user namespaces are a feature most production servers don’t use, but one that’s enabled by default. Disabling it with a single sysctl line removes an entire class of LPE attacks, not just CIFSwitch. If cifs-utils isn’t needed, removing it eliminates the attack surface for this vulnerability entirely.

FIX COMMANDS

First — check whether cifs-utils is installed on the system:

dpkg -l cifs-utils 2>/dev/null || rpm -q cifs-utils 2>/dev/null

If the package isn’t installed, the system isn’t exploitable through this vector regardless of kernel version — no further steps needed. If it is installed, read on.

If cifs-utils isn’t needed, the simplest and most reliable fix right now is to remove it:

# Debian / Ubuntu
sudo apt remove cifs-utils

# RHEL / CentOS / Rocky / AlmaLinux
sudo dnf remove cifs-utils

If cifs-utils is needed but Kerberos authentication for SMB isn’t used, neutralize the request-key rule. The following command from Manizada’s writeup overrides the rule so that key requests are immediately rejected instead of launching the helper:

cat >/etc/request-key.d/cifs.spnego.conf <<'EOF'
create cifs.spnego * * /usr/sbin/keyctl negate %k 30 %S
EOF

If SMB mounting isn’t used at all, block the cifs module from loading:

echo "install cifs /bin/true" | sudo tee /etc/modprobe.d/disable-cifs.conf

It’s also worth disabling unprivileged user namespaces — this blocks not only CIFSwitch but an entire class of similar LPEs. If the server doesn’t use containers or other features that require user namespaces:

# Debian / Ubuntu — both parameters required
sudo sysctl -w kernel.unprivileged_userns_clone=0
sudo sysctl -w user.max_user_namespaces=0
printf "kernel.unprivileged_userns_clone=0\nuser.max_user_namespaces=0\n" | sudo tee -a /etc/sysctl.d/99-hardening.conf

# RHEL / CentOS / Rocky / AlmaLinux
sudo sysctl -w user.max_user_namespaces=0
echo "user.max_user_namespaces=0" | sudo tee -a /etc/sysctl.d/99-hardening.conf

To verify that any of these mitigations actually worked, use the official PoC. After applying a mitigation, run:

git clone https://github.com/manizada/CIFSwitch
cd CIFSwitch && make && ./cifswitch

If the PoC exits without gaining root, the mitigation is working — the author officially recommends this as the validation method.

UPDATE

The definitive long-term fix is a kernel update. The upstream patch is commit 3da1fdf4efbc490041eb4f836bf596201203f8f2: it adds the cifs_spnego_key_vet_description() function, which only allows key creation when the request comes from CIFS through spnego_cred. Distributions are backporting this fix — watch your distro’s security channel and update as soon as the package is available:

# Debian / Ubuntu
sudo apt update && sudo apt upgrade

# RHEL / CentOS / Rocky / AlmaLinux
sudo dnf update kernel

A reboot is required after updating — the new kernel only takes effect after restart. To confirm the system booted with the updated kernel:

uname -r

The output shows the running kernel version — compare it against the version in your distro’s security bulletin. For a final confirmation, run the official PoC: if it exits without gaining root, the kernel contains the fix:

git clone https://github.com/manizada/CIFSwitch
cd CIFSwitch && make && ./cifswitch

Until the patch is available for your distribution, use the mitigations from the section above.

CONCLUSIONS

CIFSwitch is a reminder that “secure” doesn’t mean “verified.” The Linux kernel goes through countless audits and reviews, SELinux and AppArmor run on millions of servers — and a 19-year-old bug at the boundary between two components still made it to 2026. Not because nobody looked, but because nobody looked at both at the same time.

If you manage Linux servers, the first action today: check whether cifs-utils is present on machines where it has no business being, and remove it. Second: disable unprivileged user namespaces on servers that don’t need them. Third: watch your distro’s security channel and update the kernel as soon as the patch lands.

For those managing VPS platforms and shared hosting — this isn’t just a recommendation. The PoC is public, it works, and any tenant with shell access can try it right now. Response time is measured in hours, not days.

Leave your thought here

Your email address will not be published. Required fields are marked *