Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
tetragon-dirtyfrag — Blocking the DirtyFrag Linux LPE chain (CVE-2026-43284 / CVE-2026-43500) at runtime with a Cilium Tetragon TracingPolicy | Kitploit
Tools/GitHubGitHub/armircetaj/tetragon-dirtyfrag
Defensive ToolsExploit FrameworksVulnerability AnalysisIDS/IPS EvasionPenetration TestingLearning & EducationBinary ExploitationLabs & Practice
GitHubarmircetaj/tetragon-dirtyfrag

tetragon-dirtyfrag

Blocking the DirtyFrag Linux LPE chain (CVE-2026-43284 / CVE-2026-43500) at runtime with a Cilium Tetragon TracingPolicy

View Repository
121 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

tetragon-dirtyfrag

Blocking the DirtyFrag Linux privilege-escalation chain (CVE-2026-43284 / CVE-2026-43500) at runtime with a Cilium Tetragon TracingPolicy. The policy SIGKILLs the public proof-of-concept at its socket-setup step, before it reaches the page-cache write that would give it root.

What this is. A lab writeup. I set up Tetragon for the first time and wanted to see whether it could actually stop a real, current kernel LPE. It could. This documents exactly what I ran, what fired, and, just as importantly, the limits of what it proves. It is not a substitute for patching.

Files: this README (self-contained, evidence inline) · block-dirtyfrag.yaml (the policy)


Results

ExploitDirtyFrag - public PoC: V4bel/dirtyfrag
CVEsCVE-2026-43284 (xfrm-ESP page-cache write), CVE-2026-43500 (RxRPC page-cache write)
HostUbuntu 24.04.4 LTS, Tetragon v1.7.0 (standalone, systemd)
Baseline - no policy, kernel 6.8.0-88 (vulnerable)PoC → uid=0(root)
With policy - kernel 6.8.0-88PoC SIGKILLed at socket(AF_RXRPC); user stays uid=1000
Patched kernel 6.8.0-134PoC fails on its own (rc=4); socket hook still fires, detection, not mitigation (note)
Nature of controlCompensating control / virtual patch, not a kernel fix

What DirtyFrag is (short version)

DirtyFrag is a local privilege-escalation chain built from two independent page-cache write primitives in the Linux kernel: one in the xfrm/ESP (IPsec) in-place decryption path (CVE-2026-43284), and one in the RxRPC path (CVE-2026-43500). Each lets an unprivileged local user write attacker-controlled bytes into read-only page-cache pages, for example the cached image of a setuid-root binary such as /bin/su - and from there obtain root. The write lands in memory only; the file on disk is never modified, so file-integrity monitoring sees nothing. It is the same bug class as Dirty Pipe and Copy Fail. The two CVEs are chained deliberately: if one path is unavailable in a given environment, the other still works.

On this host, unprivileged user namespaces are restricted by AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1), which blocks the ESP half of the chain. That leaves the RxRPC path, which opens an AF_RXRPC socket (address family 33) - and that is the step this policy kills.

The real fix is a patched kernel. Everything here is a stopgap for a host you cannot patch yet. Ubuntu shipped DirtyFrag fixes some time before this test, so this is a known N-day, not a live 0-day - the point is to show what a runtime control can do on a host that is still, for whatever reason, running a vulnerable kernel.


The policy: two chokepoints

Full policy: block-dirtyfrag.yaml. It installs two kprobes.

Hook 1 - grooming sockets (sys_socket). The load-bearing control. The exploit's RxRPC path must call socket(AF_RXRPC, …) on every run, whether or not any kernel module is already loaded. The policy matches on the address family:

  • Family 33 (AF_RXRPC) → Sigkill. On any host that is not an AFS client, effectively nothing legitimate opens an AF_RXRPC socket, so a blanket kill here is safe and high-confidence.
  • Family 38 (AF_ALG) → Post (audit only, no kill). AF_ALG is the userspace kernel-crypto API and does have legitimate users (cryptsetup, libkcapi tooling, some FIPS workflows). Killing on family alone would cause false positives, so this leg only logs. The path to enforcement is: audit for a while, build a NotIn allowlist from the binaries actually observed, then consider promoting to Sigkill.

Hook 2 - vulnerable module autoload (security_kernel_module_request). Defence in depth. Fires when the kernel is asked to auto-load a vulnerable module family (esp4, esp6, rxrpc, the net-pf-33/net-pf-38 socket aliases, the pcbc/fcrypt crypto templates). Limitation: it only fires when the module is not already resident — after the first exploit run in a boot, those modules are loaded and this hook goes silent. It's a cold-boot layer, not the primary control.

Together: Hook 1 catches the exploit whether modules are warm or cold; Hook 2 adds an earlier, more specific trip on a cold host.


Test environment & reproducibility (read before trusting the result)

  • The mitigation result is on kernel 6.8.0-88-generic, which is vulnerable (the baseline reaches root). The box also has 6.8.0-134-generic installed — the patched kernel — and this was confirmed directly: rebooting into -134 makes the PoC fail on its own (rc=4, no root), with or without the policy (see the note below). To reproduce the mitigation demo, boot 6.8.0-88 from GRUB (it's still installed) — on any patched kernel there is nothing to mitigate.
  • This is one host, one boot. It demonstrates the mechanism; it is not a false-positive study. Before enforcing anything like this in production, run it in audit against representative workloads first - especially the AF_ALG leg.
  • BPF LSM is not enabled here (active LSMs: lockdown,capability,landlock,yama,apparmor — no bpf). So enforcement uses Sigkill from the kprobe, not an in-kernel LSM deny. The kill lands at the socket() syscall — before the write primitive — so it terminates the process at a mandatory early step rather than "blocking the vulnerability" itself.

Walkthrough

Steps 1–5 are all from the same 6.8.0-88 boot (the vulnerable kernel).

1. Environment - Ubuntu 24.04.4, kernel 6.8.0-88-generic, Tetragon v1.7.0 active under systemd, unprivileged userns restricted.

root@kitploit:~
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active

2. Preconditions (policy OFF) - clean starting point: no vulnerable modules resident, no xfrm state, no policy loaded.

root@kitploit:~
$ lsmod | grep -E 'esp4|esp6|rxrpc' || echo "(none loaded)"
(none loaded)
$ sudo ip xfrm state          # (empty)
$ sudo tetra tp list
ID   NAME   STATE   FILTERID   NAMESPACE   SENSORS   KERNELMEMORY   MODE   NPOST   NENFORCE   NMONITOR

3. Baseline (policy OFF) - the exploit works. This is what proves the kernel is actually vulnerable; the whole writeup rests on it.

root@kitploit:~
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)

4. Load the policy.

root@kitploit:~
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added

5. Enforced (policy ON) - the kill. The exploit is terminated by signal at the socket() call and never reaches su:

root@kitploit:~
🚀 process /home/…/dirtyfrag/exp
❓ syscall /home/…/dirtyfrag/exp __x64_sys_socket
Kernel:
   0x0: __x64_sys_socket+0x5
   0x0: do_syscall_64+0x7f
   0x0: entry_SYSCALL_64_after_hwframe+0x78
💥 exit    /home/…/dirtyfrag/exp  SIGKILL

The structured event confirms both the match and the kill - a process_kprobe on the socket syscall, then a process_exit by signal for the same PID:

root@kitploit:~
// process_kprobe — the AF_RXRPC match
{ "process_kprobe": {
    "process": { "pid": 24083, "uid": 1000, "binary": ".../dirtyfrag/exp" },
    "function_name": "__x64_sys_socket",
    "args": [ { "int_arg": 33, "label": "family" } ],
    "policy_name": "block-dirtyfrag",
    "action": "KPROBE_ACTION_POST"
} }
// process_exit — same PID, killed by signal
{ "process_exit": {
    "process": { "pid": 24083, "binary": ".../dirtyfrag/exp" },
    "signal": "SIGKILL"
} }

The kprobe's action reads KPROBE_ACTION_POST because the Post action is what emits the visible event; the Sigkill action is what produces the separate SIGKILL exit. The two events together are the proof, the match and the kill.

Note: the raw 07 capture also carries a message string from an earlier revision of the policy (before the AF_ALG leg was split to audit-only). The family: 33 match and the resulting SIGKILL are identical across revisions; only that text differs.


A note on the patched kernel (6.8.0-134)

After the vulnerable-kernel run, the host was rebooted into 6.8.0-134-generic (Ubuntu's patched kernel) and the PoC was run again:

root@kitploit:~
$ ./exp                     # policy OFF
dirtyfrag: failed (rc=4)    # exploit fails on its own — no root
$ ./exp                     # policy ON
Killed                      # SIGKILL at socket(AF_RXRPC)

Being precise about what this shows:

  • It is not a mitigation result. With the policy off, the exploit already fails (rc=4, no root) because the kernel is patched. There is nothing for the policy to prevent. The before/after that carries the mitigation claim exists only on the vulnerable -88 kernel.
  • What it does show is that the socket hook is kernel-version-independent: the PoC still calls socket(AF_RXRPC), so Tetragon still SIGKILLs it and surfaces the attempt in the log, on a patched kernel just as on an unpatched one. That has value as detection of an attempt and as defence in depth, but it is not the same as stopping a working exploit.

Limitations & honest caveats

  • Hook 1 (family 33) assumes the host is not an AFS client. On an AFS client, AF_RXRPC is legitimate and a blanket kill would break it. Verify per node.
  • The AF_ALG leg is audit-only by design. A variant using only the AF_ALG path with modules already warm would be logged, not killed, until you promote that leg to enforce (after allowlisting).
  • Hook 2 is dormant when the modules are already loaded - it only trips on a cold host.
  • The kill happens at the socket syscall. It works because, in this PoC, the AF_RXRPC socket is opened before the write primitive. That ordering is what makes an early kill effective; it is not a guarantee about every possible exploit.

References

  • PoC & author writeup - https://github.com/V4bel/dirtyfrag
  • Ubuntu CVE tracker - https://ubuntu.com/security/CVE-2026-43284 · https://ubuntu.com/security/CVE-2026-43500
  • Red Hat bulletin RHSB-2026-003 (covers both CVEs)
  • Tetragon docs - https://tetragon.io/docs/

Setup: Tetragon v1.7.0 standalone, started via systemctl; policies loaded with tetra tracingpolicy add.

Download Tool