Add cls_route4 CVE-2022-2588 module (detect-only)

11th module. net/sched cls_route4 handle-zero dead UAF — discovered
by kylebot Aug 2022, fixed mainline 5.20 (commit 9efd23297cca).
Bug existed since 2.6.39 → very wide attack surface.

- modules/cls_route4_cve_2022_2588/iamroot_modules.{c,h}:
  - kernel_range thresholds: 5.4.213 / 5.10.143 / 5.15.69 / 5.18.18 /
    5.19.7 / mainline 5.20+
  - can_unshare_userns() probes user_ns+net_ns clone availability
    (the exploit's CAP_NET_ADMIN-in-userns gate)
  - cls_route4_module_available() checks /proc/modules
  - Reports VULNERABLE if kernel in range AND user_ns allowed;
    PRECOND_FAIL if user_ns denied; OK if patched.
  - Exploit stub returns IAMROOT_PRECOND_FAIL with reference to
    kylebot's public PoC.
  - Auditd rule: tc-style sendto syscalls (rough; legit traffic
    shaping will trip — tune by user).

iamroot.c + Makefile + core/registry.h wired. CVES.md row added.

Verified on kctf-mgr (6.12.86): module reports OK, total module
count = 11.
This commit is contained in:
2026-05-16 20:33:14 -04:00
parent fe33400f94
commit 3ad1446489
6 changed files with 192 additions and 1 deletions
+1
View File
@@ -29,6 +29,7 @@ Status legend:
| CVE-2021-4034 | Pwnkit — pkexec argv[0]=NULL → env-injection | LPE (userspace setuid binary) | polkit 0.121 (2022-01-25) | `pwnkit` | 🟢 | Full detect + exploit (canonical Qualys-style: gconv-modules + execve NULL-argv). Detect handles both polkit version formats (legacy "0.105" + modern "126"). Exploit compiles payload via target's gcc → falls back gracefully if no cc available. Cleanup nukes /tmp/iamroot-pwnkit-* workdirs. **First userspace LPE in IAMROOT**. Ships auditd + sigma rules. | | CVE-2021-4034 | Pwnkit — pkexec argv[0]=NULL → env-injection | LPE (userspace setuid binary) | polkit 0.121 (2022-01-25) | `pwnkit` | 🟢 | Full detect + exploit (canonical Qualys-style: gconv-modules + execve NULL-argv). Detect handles both polkit version formats (legacy "0.105" + modern "126"). Exploit compiles payload via target's gcc → falls back gracefully if no cc available. Cleanup nukes /tmp/iamroot-pwnkit-* workdirs. **First userspace LPE in IAMROOT**. Ships auditd + sigma rules. |
| CVE-2024-1086 | nf_tables — `nft_verdict_init` cross-cache UAF | LPE (kernel arbitrary R/W via slab UAF) | mainline 6.8-rc1 (Jan 2024) | `nf_tables` | 🔵 | Detect-only. Branch-backport ranges checked (6.7.2 / 6.6.13 / 6.1.74 / 5.15.149 / 5.10.210 / 5.4.269). Also checks unprivileged user_ns clone availability (the exploit's trigger gate) — reports PRECOND_FAIL if userns is locked down even when the kernel is vulnerable. Full Notselwyn-style exploit is the next nf_tables commit. | | CVE-2024-1086 | nf_tables — `nft_verdict_init` cross-cache UAF | LPE (kernel arbitrary R/W via slab UAF) | mainline 6.8-rc1 (Jan 2024) | `nf_tables` | 🔵 | Detect-only. Branch-backport ranges checked (6.7.2 / 6.6.13 / 6.1.74 / 5.15.149 / 5.10.210 / 5.4.269). Also checks unprivileged user_ns clone availability (the exploit's trigger gate) — reports PRECOND_FAIL if userns is locked down even when the kernel is vulnerable. Full Notselwyn-style exploit is the next nf_tables commit. |
| CVE-2021-3493 | Ubuntu overlayfs userns file-capability injection | LPE (host root via file caps in userns-mounted overlayfs) | Ubuntu USN-4915-1 (Apr 2021) | `overlayfs` | 🔵 | Detect-only. **Ubuntu-specific** (vanilla upstream didn't enable userns-overlayfs-mount until 5.11). Detect: parses /etc/os-release for ID=ubuntu, checks unprivileged_userns_clone sysctl, AND with `--active` actually attempts the userns+overlayfs mount as a fork-isolated probe. Reports OK on non-Ubuntu, PRECOND_FAIL if userns locked down. Ships auditd rules covering mount(overlay) + setxattr(security.capability). | | CVE-2021-3493 | Ubuntu overlayfs userns file-capability injection | LPE (host root via file caps in userns-mounted overlayfs) | Ubuntu USN-4915-1 (Apr 2021) | `overlayfs` | 🔵 | Detect-only. **Ubuntu-specific** (vanilla upstream didn't enable userns-overlayfs-mount until 5.11). Detect: parses /etc/os-release for ID=ubuntu, checks unprivileged_userns_clone sysctl, AND with `--active` actually attempts the userns+overlayfs mount as a fork-isolated probe. Reports OK on non-Ubuntu, PRECOND_FAIL if userns locked down. Ships auditd rules covering mount(overlay) + setxattr(security.capability). |
| CVE-2022-2588 | net/sched cls_route4 handle-zero dead UAF | LPE (kernel UAF in cls_route4 filter remove) | mainline 5.20 / 5.19.7 (Aug 2022) | `cls_route4` | 🔵 | Detect-only. Branch-backport thresholds: 5.4.213 / 5.10.143 / 5.15.69 / 5.18.18 / 5.19.7. Bug exists since 2.6.39 — very wide surface. Detect also probes user_ns+net_ns clone availability; locked-down hosts report PRECOND_FAIL. Full exploit (kylebot-style: tc filter add+rm + spray + cred overwrite) follows. |
| CVE-TBD | Fragnesia (ESP shared-frag in-place encrypt) | LPE (page-cache write) | mainline TBD | `_stubs/fragnesia_TBD` | ⚪ | Stub. Per `findings/audit_leak_write_modprobe_backups_2026-05-16.md`, requires CAP_NET_ADMIN in userns netns — may or may not be in-scope depending on target environment. | | CVE-TBD | Fragnesia (ESP shared-frag in-place encrypt) | LPE (page-cache write) | mainline TBD | `_stubs/fragnesia_TBD` | ⚪ | Stub. Per `findings/audit_leak_write_modprobe_backups_2026-05-16.md`, requires CAP_NET_ADMIN in userns netns — may or may not be in-scope depending on target environment. |
## Operations supported per module ## Operations supported per module
+6 -1
View File
@@ -56,10 +56,15 @@ OVL_DIR := modules/overlayfs_cve_2021_3493
OVL_SRCS := $(OVL_DIR)/iamroot_modules.c OVL_SRCS := $(OVL_DIR)/iamroot_modules.c
OVL_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(OVL_SRCS)) OVL_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(OVL_SRCS))
# Family: cls_route4 (CVE-2022-2588)
CR4_DIR := modules/cls_route4_cve_2022_2588
CR4_SRCS := $(CR4_DIR)/iamroot_modules.c
CR4_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(CR4_SRCS))
# Top-level dispatcher # Top-level dispatcher
TOP_OBJ := $(BUILD)/iamroot.o TOP_OBJ := $(BUILD)/iamroot.o
ALL_OBJS := $(TOP_OBJ) $(CORE_OBJS) $(CFF_OBJS) $(DP_OBJS) $(EB_OBJS) $(PK_OBJS) $(NFT_OBJS) $(OVL_OBJS) ALL_OBJS := $(TOP_OBJ) $(CORE_OBJS) $(CFF_OBJS) $(DP_OBJS) $(EB_OBJS) $(PK_OBJS) $(NFT_OBJS) $(OVL_OBJS) $(CR4_OBJS)
.PHONY: all clean debug static help .PHONY: all clean debug static help
+1
View File
@@ -26,5 +26,6 @@ void iamroot_register_entrybleed(void);
void iamroot_register_pwnkit(void); void iamroot_register_pwnkit(void);
void iamroot_register_nf_tables(void); void iamroot_register_nf_tables(void);
void iamroot_register_overlayfs(void); void iamroot_register_overlayfs(void);
void iamroot_register_cls_route4(void);
#endif /* IAMROOT_REGISTRY_H */ #endif /* IAMROOT_REGISTRY_H */
+1
View File
@@ -345,6 +345,7 @@ int main(int argc, char **argv)
iamroot_register_pwnkit(); iamroot_register_pwnkit();
iamroot_register_nf_tables(); iamroot_register_nf_tables();
iamroot_register_overlayfs(); iamroot_register_overlayfs();
iamroot_register_cls_route4();
enum mode mode = MODE_SCAN; enum mode mode = MODE_SCAN;
struct iamroot_ctx ctx = {0}; struct iamroot_ctx ctx = {0};
@@ -0,0 +1,171 @@
/*
* cls_route4_cve_2022_2588 — IAMROOT module
*
* net/sched cls_route4 dead UAF: when a route4 filter with handle==0
* is removed, the corresponding hashtable bucket may keep a stale
* pointer to the freed filter. Subsequent traffic-class lookup
* follows the dangling pointer → kernel UAF.
*
* Discovered by kylebot / xkernel (Aug 2022). Mainline fix
* 9efd23297cca "net_sched: cls_route: remove from list when handle
* is 0" (Aug 2022). Bug existed since 2.6.39 — very wide
* vulnerability surface.
*
* STATUS: 🔵 DETECT-ONLY. Public exploits exist; porting is
* follow-up.
*
* Exploitation preconditions:
* - cls_route4 module compiled in / loadable (CONFIG_NET_CLS_ROUTE4)
* - CAP_NET_ADMIN (usually obtained via user_ns + map-root-to-uid)
* - unprivileged_userns_clone=1 if going the userns route
*
* Affected kernel ranges (vulnerable < these):
* 5.4.x : K < 5.4.213
* 5.10.x : K < 5.10.143
* 5.15.x : K < 5.15.69
* 5.18.x : K < 5.18.18
* 5.19.x : K < 5.19.7
* Mainline 5.20+ / 6.0+ : patched (the fix landed before 5.20-rc)
*/
#include "iamroot_modules.h"
#include "../../core/registry.h"
#include "../../core/kernel_range.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <sys/wait.h>
static const struct kernel_patched_from cls_route4_patched_branches[] = {
{5, 4, 213},
{5, 10, 143},
{5, 15, 69},
{5, 18, 18},
{5, 19, 7},
{5, 20, 0}, /* mainline */
};
static const struct kernel_range cls_route4_range = {
.patched_from = cls_route4_patched_branches,
.n_patched_from = sizeof(cls_route4_patched_branches) /
sizeof(cls_route4_patched_branches[0]),
};
static bool cls_route4_module_available(void)
{
/* Check /proc/modules for currently-loaded cls_route4. Even when
* not loaded, autoload may bring it in on first tc qdisc add — we
* conservatively treat "not loaded now" as "potentially available". */
FILE *f = fopen("/proc/modules", "r");
if (!f) return false;
char line[512];
bool found = false;
while (fgets(line, sizeof line, f)) {
if (strncmp(line, "cls_route4 ", 11) == 0) { found = true; break; }
}
fclose(f);
return found;
}
static int can_unshare_userns(void)
{
pid_t pid = fork();
if (pid < 0) return -1;
if (pid == 0) {
if (unshare(CLONE_NEWUSER | CLONE_NEWNET) == 0) _exit(0);
_exit(1);
}
int status;
waitpid(pid, &status, 0);
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
static iamroot_result_t cls_route4_detect(const struct iamroot_ctx *ctx)
{
struct kernel_version v;
if (!kernel_version_current(&v)) {
fprintf(stderr, "[!] cls_route4: could not parse kernel version\n");
return IAMROOT_TEST_ERROR;
}
/* Bug-introduction predates anything we'd reasonably scan; if the
* kernel is below the oldest LTS we model (5.4), still report
* vulnerable. */
bool patched = kernel_range_is_patched(&cls_route4_range, &v);
if (patched) {
if (!ctx->json) {
fprintf(stderr, "[+] cls_route4: kernel %s is patched\n", v.release);
}
return IAMROOT_OK;
}
/* Module + userns preconditions. */
bool nft_loaded = cls_route4_module_available();
int userns_ok = can_unshare_userns();
if (!ctx->json) {
fprintf(stderr, "[i] cls_route4: kernel %s in vulnerable range\n", v.release);
fprintf(stderr, "[i] cls_route4: cls_route4 module currently loaded: %s\n",
nft_loaded ? "yes" : "no (may autoload)");
fprintf(stderr, "[i] cls_route4: unprivileged user_ns + net_ns clone: %s\n",
userns_ok == 1 ? "ALLOWED" :
userns_ok == 0 ? "DENIED" : "could not test");
}
/* If userns is locked down, unprivileged-LPE path is closed.
* Kernel still needs patching though — report PRECOND_FAIL so the
* verdict isn't "VULNERABLE" but the issue isn't masked. */
if (userns_ok == 0) {
if (!ctx->json) {
fprintf(stderr, "[+] cls_route4: user_ns denied → unprivileged exploit unreachable\n");
}
return IAMROOT_PRECOND_FAIL;
}
if (!ctx->json) {
fprintf(stderr, "[!] cls_route4: VULNERABLE — kernel in range AND user_ns allowed\n");
}
return IAMROOT_VULNERABLE;
}
static iamroot_result_t cls_route4_exploit(const struct iamroot_ctx *ctx)
{
(void)ctx;
fprintf(stderr,
"[-] cls_route4: exploit not yet implemented in IAMROOT.\n"
" Status: 🔵 DETECT-ONLY. Reference: kylebot's public PoC.\n"
" Exploit: tc filter add ... route4 handle 0; then remove;\n"
" spray to refill the freed slot; trigger via traffic class\n"
" lookup; cred overwrite or modprobe_path hijack.\n");
return IAMROOT_PRECOND_FAIL;
}
static const char cls_route4_auditd[] =
"# cls_route4 dead UAF (CVE-2022-2588) — auditd detection rules\n"
"# Flag tc filter operations with route4 classifier from non-root.\n"
"# False positives: legitimate traffic-shaping setup. Tune by user.\n"
"-a always,exit -F arch=b64 -S sendto -F a3=0x10 -k iamroot-cls-route4\n";
const struct iamroot_module cls_route4_module = {
.name = "cls_route4",
.cve = "CVE-2022-2588",
.summary = "net/sched cls_route4 handle-zero dead UAF → kernel R/W",
.family = "cls_route4",
.kernel_range = "2.6.39 ≤ K, fixed mainline 5.20; backports: 5.4.213 / 5.10.143 / 5.15.69 / 5.18.18 / 5.19.7",
.detect = cls_route4_detect,
.exploit = cls_route4_exploit,
.mitigate = NULL, /* mitigation: blacklist cls_route4 module OR disable user_ns */
.cleanup = NULL,
.detect_auditd = cls_route4_auditd,
.detect_sigma = NULL,
.detect_yara = NULL,
.detect_falco = NULL,
};
void iamroot_register_cls_route4(void)
{
iamroot_register(&cls_route4_module);
}
@@ -0,0 +1,12 @@
/*
* cls_route4_cve_2022_2588 — IAMROOT module registry hook
*/
#ifndef CLS_ROUTE4_IAMROOT_MODULES_H
#define CLS_ROUTE4_IAMROOT_MODULES_H
#include "../../core/module.h"
extern const struct iamroot_module cls_route4_module;
#endif