Skip to main content

Fragnesia Makes Three: Why It Couldn't Touch Workloads Under a Tailored AppArmor Profile Either

Fragnesia (CVE-2026-46300) is the third universal Linux kernel LPE in the Dirty Frag class to land in under two weeks. It abuses the XFRM ESP-in-TCP path to write into the page cache of read-only files with no race condition, and ships with a one-line public exploit. For workloads running under a behaviour-generated AppArmor profile, the surface it needs was never in the allow list. This is the same non-event, a third time. Here's why, and how to confirm your exposure in seconds.

Hannes Ullman

Hannes Ullman

bifrost security

Fragnesia Makes Three: Why It Couldn't Touch Workloads Under a Tailored AppArmor Profile Either

We ended the Dirty Frag post with a prediction: there would be a third one, and it would land the same way. It took six days.

Fragnesia (CVE-2026-46300, CVSS 7.8) was disclosed by William Bowling and the V12 team as a member of the Dirty Frag vulnerability class, a separate bug with its own upstream patch in the same code surface as Copy Fail and Dirty Frag before it. The effect is the same as its siblings: an unprivileged process can write a chosen byte into the page-cache copy of any readable file, one byte per trigger, deterministically, with no race to win. The public PoC uses this to overwrite the cached copy of /usr/bin/su with a root-shell stub and execute it, never touching the on-disk binary.

As with the previous two, this is a local privilege escalation, not a remote exploit. The attacker has to land in your container first, through a compromised dependency, an app RCE, leaked credentials, or any other initial-access vector. Fragnesia is the tool they reach for once inside to turn a low-privilege foothold into root on the host, and from the host, every pod on the node.

What’s new here isn’t the mechanism. It’s the cadence. Three universal local-roots in the same bug class in under two weeks, each shipping a public, weaponised, copy-paste PoC at disclosure, none requiring a race condition. The time-to-exploit window for this class has collapsed to zero. Patching a Kubernetes fleet still realistically takes days to weeks. That gap is precisely where runtime enforcement earns its keep.

A chokepoint we’ve already seen

The interesting thing about Fragnesia is how little is new. Strip it to its required surface and it is, almost line for line, the Dirty Frag ESP variant with the transport swapped from ESP-in-UDP to ESP-in-TCP. The chain needs an unprivileged user namespace to get CAP_NET_ADMIN, NETLINK_XFRM to install an ESP-in-TCP security association, and the kernel crypto API to arm the in-place decrypt. A bifrost-generated profile only allows what a workload actually exercised during its learning window, and none of these were in the observed set for a normal application then, and they aren’t now:

  • Unprivileged user namespaces are denied by the userns rule. This is the load-bearing denial: the exploit’s very first syscall fails, and the chain dies at line one. The few containers that legitimately nest namespaces, some build runners and CI jobs, surface on profile review rather than slipping through silently.
  • NETLINK_XFRM is denied unless the workload registered XFRM transforms during the learning window, which application workloads don’t. No security association, no ESP-in-TCP, no decrypt path reached. Either of these two denials alone breaks the chain.
  • AF_ALG, the kernel-crypto chokepoint Copy Fail hinged on, stays denied too. Treat this one as defence in depth: a determined author could implement the crypto in userspace and skip it. The kills are userns and XFRM netlink.

There’s a detail in the PoC’s own instructions worth dwelling on. To run it on Ubuntu, V12 tell you to first disable AppArmor’s restriction on unprivileged user namespaces, because it already blocks them by default and is in the way. That toggle needs host-level privilege a confined container does not have. The exploit’s documented bypass for AppArmor is itself unavailable from inside a properly confined workload. The profile just closes the door per-workload and removes the global toggle from the equation.

Why allow-listing keeps winning (still)

Fragnesia exists as a separate bug specifically because the Dirty Frag patch and the esp4/esp6 modprobe blacklist are per-bug mitigations, and the next bug needs its own. That is the treadmill we keep describing: mitigation by enumeration of bad does not scale. Each new variant forces a new blacklist, a new patch, a new fire drill.

A default-deny profile never enumerated those modules, families, or syscalls in the first place. There is nothing to bypass when the default answer is no. The good list never included unprivileged userns, XFRM netlink, or AF_ALG, so it didn’t need touching when Copy Fail dropped, when Dirty Frag dropped, or when Fragnesia dropped six days later.

This is the behaviour allow-listing thesis playing out a third time in two weeks. You don’t need to predict tomorrow’s CVE. You need the profile to reflect what the app actually does.

Assess your exposure in seconds

In bifrost, every running workload is continuously analysed against its allow-listed profile, and that profile is mapped against the syscall, capability, and network-family requirements of known CVEs. To check your exposure to Fragnesia:

  1. Open the bifrost dashboard.
  2. Search for CVE-2026-46300.
  3. Read the per-workload verdict.

The expected output is a list of every protected workload showing not exploitable, because no profile in the cluster permits the combination of unprivileged userns and XFRM netlink the chain depends on. Hand the view to your CISO, confirm the kernel patch can roll out at a normal cadence rather than as a fire drill, and stop carrying the CVE as a P0.

Two caveats, kept honest

This claim survives scrutiny only if it’s stated precisely.

Node-level coverage matters. Fragnesia compromises the host. If your application pods are profiled but a sidecar, an unconfined DaemonSet, or a CI runner on the same node lacks a profile, the kernel is reachable from that process, and the resulting root compromise affects every pod on the node. Fragnesia is unreachable for any workload under a bifrost-generated profile, and if you cover the cluster comprehensively, it’s unreachable cluster-wide.

Some workloads legitimately need IPsec. A handful of service-mesh implementations and CNI plugins encrypt node-to-node traffic via IPsec, which requires XFRM netlink. Those workloads will have the surface in their learned profile and still need the host kernel patched. For everything that isn’t doing in-kernel IPsec, which is the overwhelming majority of application workloads, neither chokepoint is granted.

The takeaway

Three local-roots, one bug class, two weeks, and the same profile stopped all three at line one without a single edit. That is the entire argument for runtime enforcement as a first line of defence: in a world where the gap between disclosure and a working public exploit has shrunk to nothing, the control that protects you cannot be one you author after the CVE lands. It has to already be true.

There will be a fourth one. It will ship with a public PoC, it will need a surface no normal workload exercises, and it will land the same way.

Tags

apparmor container security kubernetes cve runtime

Ready to see runtime security in action?

bifrost automatically generates tailored security profiles for your containers and cuts CVE noise by up to 90%. Free trial, no credit card required.