← Writing

Fragnesia CVE-2026-46300: Linux Kernel Fixes and Running-Kernel Evidence

On this page

Fragnesia (CVE-2026-46300) is a local privilege escalation vulnerability in the Linux kernel. Apply the fixes for the correct distribution and kernel family, then verify that they are active in the running kernel. Ubuntu’s official description also notes possible container escape under applicable conditions. Running Linux does not mean every system has the same exposure.

Does this directly give an internet attacker root?

This CVE is a local privilege escalation. It requires an attacker’s ability to run code on the system and reach the vulnerable kernel path. Distinguish it from remote code execution in a web application. CISA’s official KEV data records the risk of chaining it with PHP2Shell CVE-2026-109618 and CVE-2026-109619. Check PHP runtime patching and host-kernel patching separately.

The original CVE record describes a shared-page-fragment marker lost during socket-buffer coalescing. Later ESP processing can mistake shared pages for private ones and modify them in place. The CVE was published on May 23, 2026; CISA added it to KEV on October 28. This is not a defect first introduced in October. Interpret KEV’s October 31 deadline within the applicable directive’s scope, rather than as a universal change deadline for all services.

Which kernel needs updating?

Do not narrow the fix scope to one CVE in isolation. Ubuntu’s security-team notes advise shipping the related fixes together with CVE-2026-43284. Another fragment-transfer-path fix is also tracked as CVE-2026-43503. CVE-2026-43284 is a separate Dirty Frag-family entry. Prefer the supplier’s complete fixed package; applying one selected commit does not establish completion.

The following are selected Ubuntu linux source-package fix thresholds checked on October 31, 2026. First map the installed binary package to its source package and kernel family.

Ubuntu release linux fix threshold
22.04 LTS 5.15.0-181.191
24.04 LTS 6.8.0-124.124
26.04 LTS 7.0.0-22.22

Do not reuse these thresholds for AWS, Azure, HWE, OEM or real-time kernels: package names, supported families and supplier backports differ. For RHEL, follow the product status and RHSA linked from Red Hat’s bulletin. AWS’s own bulletin explains the unaffected status of Amazon Linux and Bottlerocket based on their espintcp configuration. Do not extend that statement to arbitrary custom kernels or other distributions running on EC2.

Collect evidence of the running kernel first

The collector checks for Linux, reads the running kernel and OS, queries two package names if dpkg-query exists, distinguishes failures and outputs evidence without assessing patch status

The diagram follows the collector below. It requires Python 3.10 or later. On Linux with dpkg-query, it queries only two package names derived from the running kernel release. It uses no network access, administrator privileges, module loading or system changes. It is neither a vulnerability detector nor a patch-completion verdict. Missing evidence is never reported as safe.

kernel-evidence.py

import json
import platform
import shutil
import subprocess


def collect():
    result = {"patch_status": "not-assessed", "scope": "current-execution-context"}
    if platform.system() != "Linux":
        return {**result, "error": "linux-required"}, 2
    try:
        release = platform.release()
        distro = platform.freedesktop_os_release()
    except OSError:
        return {**result, "error": "os-evidence-unavailable"}, 2
    result.update(running_kernel=release,
                  os={key: distro.get(key) for key in ("ID", "VERSION_ID")}, queries=[])
    command = shutil.which("dpkg-query")
    if not command:
        return {**result, "package_query": "dpkg-query-unavailable"}, 0
    result["package_query"] = "dpkg-query"
    for name in ("linux-image-" + release, "linux-image-unsigned-" + release):
        row = {"package": name}
        try:
            answer = subprocess.run(
                [command, "-W", "-f=${Package}\t${Version}\t${db:Status-Status}\n", name],
                capture_output=True, text=True, timeout=5, check=False)
        except (OSError, subprocess.TimeoutExpired):
            row["state"] = "query-error"
        else:
            fields = answer.stdout.rstrip("\n").split("\t")
            if answer.returncode == 1:
                row["state"] = "not-found"
            elif answer.returncode != 0:
                row["state"] = "query-error"
            elif len(fields) != 3 or fields[0] != name or not fields[1]:
                row["state"] = "unexpected-output"
            elif fields[2] != "installed":
                row["state"] = "not-installed"
            else:
                row.update(state="installed", version=fields[1])
        result["queries"].append(row)
    return result, 0


if __name__ == "__main__":
    evidence, status = collect()
    print(json.dumps(evidence, ensure_ascii=False, indent=2, sort_keys=True))
    raise SystemExit(status)
python3 kernel-evidence.py

A non-Linux environment or unavailable OS evidence returns exit code 2. Even when Linux collection completes, an individual package query may fail: read package_query and each state. Exit code 0 does not establish patch completion. These two queries cannot identify the owning package for RPM or custom kernels.

The following output was executed and checked with synthetic inputs: Ubuntu 24.04, running kernel 6.8.0-124-generic, the first package installed and the second absent. It is not evidence from a production Ubuntu host. Execution checks reproduce this output and query-failure branches; they do not test actual kernel vulnerability, patch adoption or exploitation.

{
  "os": {
    "ID": "ubuntu",
    "VERSION_ID": "24.04"
  },
  "package_query": "dpkg-query",
  "patch_status": "not-assessed",
  "queries": [
    {
      "package": "linux-image-6.8.0-124-generic",
      "state": "installed",
      "version": "6.8.0-124.124"
    },
    {
      "package": "linux-image-unsigned-6.8.0-124-generic",
      "state": "not-found"
    }
  ],
  "running_kernel": "6.8.0-124-generic",
  "scope": "current-execution-context"
}

Inside a container, the OS and package database may belong to the container while the kernel belongs to the shared host. Collect again through the host or VM management path. A newly installed kernel package can also differ from the kernel currently running. This collector queries only names tied to the current release; it does not enumerate every installed kernel. It does not verify package signatures, integrity or livepatch status.

What should you verify after patching?

  1. Map the asset’s distribution, running kernel and package family to the supplier’s CVE or security notice. Do not compare versions across different families with a simple string comparison.
  2. Apply fixed packages through supported repositories or images, then replace or reboot nodes in stages. Ubuntu USN-8388-1 calls for a reboot after a standard update and addresses ABI changes for third-party kernel modules. If relying on livepatch alone, separately verify support and actual application for the CVE and running kernel.
  3. Collect the same evidence after restart, compare the active kernel against the supplier’s fix evidence, and check service health, storage and networking. Restarting a Kubernetes pod alone does not replace the node kernel.
  4. Investigate possible initial access and persistence too. Successful patching does not prove the absence of an existing webshell or stolen credentials.

If an immediate update is not possible, consult Ubuntu’s temporary mitigation guidance and your supplier’s instructions. Module restrictions may conflict with features such as VPN/IPsec; already loaded or built-in paths require separate assessment. Absence from a module listing does not establish unaffected status. Reducing exposure and restricting untrusted code are temporary measures, not substitutes for applying fixed packages.

Discussion

View on GitHub ↗