Skip to content
Cifswitch

Cifswitch

heyitsas.im May 30, 2026

TLDR: A distro-specific Linux LPE found by harnessing LLMs into better multihop knowledge composition. Read on for affected distros, mitigations, and vulnerability details.

In Getting LLMs Drunk to Find Remote Linux Kernel OOB Writes (and More) , I’d mentioned how improving LLMs’ ability to compose existing knowledge is a promising avenue for unlocking “creative” – or at least non-trivial – vulnerability findings. Incidentally, among the latest slew of Linux LPEs, CopyFail stood out for – among other things – exquisitely composing several logic bugs, serving as a reminder of the massive potential value of the approach. Unfortunately, training a capable looped transformer to improve compositionality was a non-starter, so I started looking for harness-level improvements instead.

GraphWalk: Enabling Reasoning in Large Language Models through Tool-Based Graph offered a promising alternative: the authors developed a tool for models to traverse (and reason through) graphs, improving their multihop reasoning capabilities. The benefits were measured primarily for non-reasoning models; but, on large-enough graphs, non-reasoning models equipped with the tool outperformed reasoning models without it. So, the general approach seemed promising even for otherwise-scaffolded reasoning models – slicing the context with RLMs, .md files with “memories,” etc., are all useful for tackling graph-based problems, but we could still strengthen the harness with a first-class graph traversal tool.

The paper described a tool for existing graphs, but for vulnerability hunting we don’t actually have “interesting” graphs pre-built (CodeQL-style CPGs are too low-level/clunky for the level of abstraction I wanted)! So I harnessed the agents to a) build the graphs at a higher level of abstraction and b) actually query them, like in the paper above. The graphs were intended to capture the following (deliberately somewhat fuzzy to play to LLMs’ strengths):

At this point, I had the scaffolding to point at the desired target. Ideally, it’d be something straddling kernel and userspace for compositionality to really shine, and primarily/entirely a logic bug chain… Based on some prior experience , the SMB protocol family seemed like a fertile ground.

The harnessed agents found an issue at the intersection of kernel’s CIFS and the userspace cifs-utils -provided helper.

In short, they first discovered that the kernel did not validate the description origin of the cifs.spnego key object. Backtracking, they then found that they could therefore issue a request_key() syscall with a fake key description, which launches a rootful helper. Finally, after noticing that the fake key descriptions have actual security relevance – they contain pid , which combined with upcall_target=app controls which namespace the helper actually runs in – they converted the namespace confusion into root on the machine.

Since the patch has been out for over a week and is queued for stable, we agreed with linux-distros@ on an embargo through May 27, 2026. The advisory is now public so that the affected system owners can patch or apply other mitigations . The CVE assignment is still pending.

CIFS/SMB is a Windows-style network filesystem protocol. On Linux, the CIFS kernel client handles the actual filesystem parts: mounting the , talking SMB to the server, doing reads/writes, etc. But, understandably, for Kerberos-auth’d mounts, kernel CIFS doesn’t roll its own auth stack and instead relies on a userspace helper provided by cifs-utils .

The interaction happens through Linux keyrings. The kernel requests a cifs.spnego -type key, and the normal keyutils/request-key config runs cifs.upcall as root to fetch or build the Kerberos/SPNEGO material. That brings us to – ahem – the key part.

The expected interaction between the kernel and userspace parts is the following:

You may have already noticed the critical question: does either userspace or the kernel validate that the key description fields actually came from kernel CIFS? That’s where things break:

Now, let’s walk through what actually enables the PoC to convert the above to root:

A normal Kerberos CIFS mount eventually reaches cifs_get_spnego_key() . The kernel builds a cifs.spnego description string from kernel state, then asks the keyring subsystem for a cifs.spnego -type key under CIFS’s private spnego_cred :

These fields actually matter: uid / creduid decide whose credentials the helper should look up, while pid / upcall_target determine what the helper should treat as the application’s namespace.

But the key type definition did not enforce that the key was kernel-CIFS-originating. Before the fix, cifs_spnego_key_type was just:

(Note the missing .vet_description , the hook that would govern a given key_type ’s description’s legitimacy). So, an unprivileged process could ask for the same cifs.spnego -type key with request_key("cifs.spnego", totally_fake_description, ...) , and the default request-key rule ( create cifs.spnego * * /usr/sbin/cifs.upcall %k ) would still launch cifs.upcall as root.

Importantly, the kernel did not need to return a key for cifs.upcall to launch. The upcall launches first, enabling the attack, even if the kernel -ENOKEY s after.

On the userspace side, cifs.upcall parses the key description and treats the decoded fields as kernel-provided facts regardless of where they came from . The namespace-aware code parses upcall_target=mount / upcall_target=app and then switches namespaces when the upcall target is app :

The explains why this is a supported mode of operation; it also reveals the risk of managing to thread through an attacker-controlled arg->pid .

Only after that namespace switch does it get the target gid out of the passwd NSS database with getpwuid(uid) . The final identity transition happens later, when the helper reaches setuid(uid) and drop_all_capabilities() :

And to reiterate, getpwuid(0) goes through NSS. If the process has already switched into an attacker-controlled mount namespace, NSS can mean the following is executed as root:

At this point, libnss_pwn.so.2 can drop a sudoers.d config with the attacker’s username, as in the PoC.

The bare minimum kernel-side fix is to treat the descriptions as legitimate only when CIFS is using spnego_cred :

There’s still userspace hardening to be done to not assume that the key description is necessarily kernel-generated, but the above stops the exploitation by itself.

The exploitability conditions are all of the below:

Aside from applying the backported kernel patch , you can mitigate via any of the following:

You can use the released PoC to validate the mitigations.

A very non-exhaustive list of systems tested.

Here, cifs-utils is installed by default and the default distro config (LSMs/etc.) does not stop the exploitation:

Exploitable under default distro config, but cifs-utils needs to be installed manually:

The default distro config (LSMs/etc.) blocks exploitation even if cifs-utils is present:

The two tested cases where cifs-utils is too old to be exploitable:

Ultimately, I was curious if the models could build non-trivial, multihop chains given the right tools. By producing and walking a semantic graph of security-relevant objects and properties – not so in the weeds that they got stifled by the low-level definitions, but not so abstract that they flailed aimlessly – the models arrived at:

While the primitives themselves are not groundbreaking, the chain is pretty neat, and is much more exciting than the earlier “drunk” ksmbd memory safety findings! The graph-based approach was likely not strictly necessary, as simple Markdown memory may have sufficed, but it did seem to enable the agents to systematically burn down the potential exploit lanes with ease.

Extracted Entities