From a52f5a657f268a798d413470a687e09251c44d66 Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Sat, 16 May 2026 20:49:58 -0400 Subject: [PATCH] Phase 7: af_packet (CVE-2017-7308) + FUSE legacy (CVE-2022-0185) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two more famous LPEs broadening 'THE tool' coverage: af_packet CVE-2017-7308 (Andrey Konovalov, Mar 2017): - AF_PACKET TPACKET_V3 ring setup integer overflow → heap write-where - Fills 2017 coverage gap - kernel_range: 3.18.49 / 4.4.57 / 4.9.18 / 4.10.6 / mainline 4.11+ - Needs CAP_NET_RAW via user_ns clone - Famous as the canonical 'userns + AF_PACKET → root' research-era LPE fuse_legacy CVE-2022-0185 (William Liu / Crusaders-of-Rust, Jan 2022): - legacy_parse_param fsconfig heap OOB → cross-cache UAF → root - **Container-escape angle** — relevant to rootless docker/podman/snap (the system admin persona's nightmare) - kernel_range: 5.4.171 / 5.10.91 / 5.15.14 / 5.16.2 / mainline 5.17+ - Needs user_ns + mount_ns to reach legacy_load() code path - Originally reported as FUSE-specific but actually applies to any fs-mount path from userns (cgroup2, etc.) Both detect-only initially; full exploits in follow-ups. Coverage by year now: 2016: dirty_cow 🟢 2017: af_packet 🔵 2019: ptrace_traceme 🔵 2021: pwnkit, overlayfs, netfilter_xtcompat 🟢/🟢/🔵 2022: dirty_pipe, cls_route4, fuse_legacy 🟢/🔵/🔵 2023: entrybleed 🟢 2024: nf_tables 🔵 2026: copy_fail family (×5) 🟢 16 modules total. Build clean. Scan on kctf-mgr: 11 OK / 5 VULNERABLE. --- CVES.md | 2 + Makefile | 12 +- core/registry.h | 2 + iamroot.c | 2 + .../af_packet_cve_2017_7308/iamroot_modules.c | 138 +++++++++++++++ .../af_packet_cve_2017_7308/iamroot_modules.h | 12 ++ .../iamroot_modules.c | 163 ++++++++++++++++++ .../iamroot_modules.h | 12 ++ 8 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 modules/af_packet_cve_2017_7308/iamroot_modules.c create mode 100644 modules/af_packet_cve_2017_7308/iamroot_modules.h create mode 100644 modules/fuse_legacy_cve_2022_0185/iamroot_modules.c create mode 100644 modules/fuse_legacy_cve_2022_0185/iamroot_modules.h diff --git a/CVES.md b/CVES.md index b4d07fd..09f3839 100644 --- a/CVES.md +++ b/CVES.md @@ -33,6 +33,8 @@ Status legend: | CVE-2016-5195 | Dirty COW — COW race via /proc/self/mem + madvise | LPE (page-cache write into root-owned files) | mainline 4.9 (Oct 2016) | `dirty_cow` | 🟢 | Full detect + exploit + cleanup. **Old-systems coverage** — affects RHEL 6/7 (3.10 baseline), Ubuntu 14.04 (3.13), Ubuntu 16.04 (4.4), embedded boxes, IoT. Phil-Oester-style two-thread race: writer thread via `/proc/self/mem` vs madvise(MADV_DONTNEED) thread. Targets /etc/passwd UID flip + `su`. Ships auditd watch on /proc/self/mem + sigma rule for non-root mem-open. Pthread-linked. | | CVE-2019-13272 | PTRACE_TRACEME → setuid execve → cred escalation | LPE (kernel ptrace race; no exotic preconditions) | mainline 5.1.17 (Jun 2019) | `ptrace_traceme` | 🔵 | Detect-only. Branch backports: 4.4.182 / 4.9.182 / 4.14.131 / 4.19.58 / 5.0.20 / 5.1.17 / mainline 5.2. **Famous because works on default-config systems** — no user_ns required. jannh's PGZ disclosure, June 2019. Exploit (fork → child PTRACE_TRACEME → parent execve setuid → child ptrace-injects shellcode) follows. | | CVE-2021-22555 | iptables xt_compat heap-OOB → cross-cache UAF | LPE (kernel R/W via 4-byte heap OOB write + msg_msg/sk_buff groom) | mainline 5.12 / 5.11.10 (Apr 2021) | `netfilter_xtcompat` | 🔵 | Detect-only. Branch backports: 5.11.10 / 5.10.27 / 5.4.110 / 4.19.185 / 4.14.230 / 4.9.266 / 4.4.266. **Bug existed since 2.6.19 (2006) — 15 years of latent vulnerability**. Andy Nguyen's PGZ disclosure. Needs CAP_NET_ADMIN via user_ns. Full exploit (~400 lines msg_msg+sk_buff cross-cache groom) is substantial follow-up. | +| CVE-2017-7308 | AF_PACKET TPACKET_V3 integer overflow → heap write-where | LPE (CAP_NET_RAW via userns) | mainline 4.11 / 4.10.6 (Mar 2017) | `af_packet` | 🔵 | Detect-only. Andrey Konovalov's research-era classic. Branch backports: 4.10.6 / 4.9.18 (RHEL 7-ish era) / 4.4.57 / 3.18.49. Needs user_ns for CAP_NET_RAW. Full exploit follows. | +| CVE-2022-0185 | legacy_parse_param fsconfig heap OOB → container-escape | LPE (cross-cache UAF → cred overwrite from rootless container) | mainline 5.16.2 (Jan 2022) | `fuse_legacy` | 🔵 | Detect-only. **Container-escape angle** — relevant to rootless docker/podman/snap setups. Branch backports: 5.16.2 / 5.15.14 / 5.10.91 / 5.4.171. Needs user_ns + mount_ns. William Liu / Crusaders-of-Rust PoC reference. | | 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 diff --git a/Makefile b/Makefile index f6a0c7f..6cef10a 100644 --- a/Makefile +++ b/Makefile @@ -76,10 +76,20 @@ NXC_DIR := modules/netfilter_xtcompat_cve_2021_22555 NXC_SRCS := $(NXC_DIR)/iamroot_modules.c NXC_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(NXC_SRCS)) +# Family: af_packet (CVE-2017-7308) +AFP_DIR := modules/af_packet_cve_2017_7308 +AFP_SRCS := $(AFP_DIR)/iamroot_modules.c +AFP_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(AFP_SRCS)) + +# Family: fuse_legacy (CVE-2022-0185) +FUL_DIR := modules/fuse_legacy_cve_2022_0185 +FUL_SRCS := $(FUL_DIR)/iamroot_modules.c +FUL_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(FUL_SRCS)) + # Top-level dispatcher TOP_OBJ := $(BUILD)/iamroot.o -ALL_OBJS := $(TOP_OBJ) $(CORE_OBJS) $(CFF_OBJS) $(DP_OBJS) $(EB_OBJS) $(PK_OBJS) $(NFT_OBJS) $(OVL_OBJS) $(CR4_OBJS) $(DCOW_OBJS) $(PTM_OBJS) $(NXC_OBJS) +ALL_OBJS := $(TOP_OBJ) $(CORE_OBJS) $(CFF_OBJS) $(DP_OBJS) $(EB_OBJS) $(PK_OBJS) $(NFT_OBJS) $(OVL_OBJS) $(CR4_OBJS) $(DCOW_OBJS) $(PTM_OBJS) $(NXC_OBJS) $(AFP_OBJS) $(FUL_OBJS) .PHONY: all clean debug static help diff --git a/core/registry.h b/core/registry.h index 4f9ae3d..bd52d84 100644 --- a/core/registry.h +++ b/core/registry.h @@ -30,5 +30,7 @@ void iamroot_register_cls_route4(void); void iamroot_register_dirty_cow(void); void iamroot_register_ptrace_traceme(void); void iamroot_register_netfilter_xtcompat(void); +void iamroot_register_af_packet(void); +void iamroot_register_fuse_legacy(void); #endif /* IAMROOT_REGISTRY_H */ diff --git a/iamroot.c b/iamroot.c index e195f48..5d2ec96 100644 --- a/iamroot.c +++ b/iamroot.c @@ -349,6 +349,8 @@ int main(int argc, char **argv) iamroot_register_dirty_cow(); iamroot_register_ptrace_traceme(); iamroot_register_netfilter_xtcompat(); + iamroot_register_af_packet(); + iamroot_register_fuse_legacy(); enum mode mode = MODE_SCAN; struct iamroot_ctx ctx = {0}; diff --git a/modules/af_packet_cve_2017_7308/iamroot_modules.c b/modules/af_packet_cve_2017_7308/iamroot_modules.c new file mode 100644 index 0000000..63a7418 --- /dev/null +++ b/modules/af_packet_cve_2017_7308/iamroot_modules.c @@ -0,0 +1,138 @@ +/* + * af_packet_cve_2017_7308 — IAMROOT module + * + * AF_PACKET TPACKET_V3 ring-buffer setup integer-overflow → heap + * write-where primitive. Discovered by Andrey Konovalov (March 2017). + * + * STATUS: 🔵 DETECT-ONLY. Konovalov's public PoC works end-to-end + * — porting is a follow-up commit. + * + * Affected: kernel < 4.10.6 mainline. Stable backports: + * 4.10.x : K >= 4.10.6 + * 4.9.x : K >= 4.9.18 (LTS — RHEL 7-ish era) + * 4.4.x : K >= 4.4.57 + * 3.18.x : K >= 3.18.49 + * + * Exploitation preconditions: + * - CAP_NET_RAW (via unprivileged user_ns) to create AF_PACKET socket + * - CONFIG_PACKET=y (almost always — even container kernels) + * + * Why famous: was the canonical "userns + AF_PACKET → root" chain for + * Konovalov's research era. Many other AF_PACKET bugs followed (e.g. + * CVE-2020-14386) sharing the same userns-clone gate. + */ + +#include "iamroot_modules.h" +#include "../../core/registry.h" +#include "../../core/kernel_range.h" + +#include +#include +#include +#include +#include +#include + +static const struct kernel_patched_from af_packet_patched_branches[] = { + {3, 18, 49}, + {4, 4, 57}, + {4, 9, 18}, + {4, 10, 6}, + {4, 11, 0}, /* mainline */ +}; + +static const struct kernel_range af_packet_range = { + .patched_from = af_packet_patched_branches, + .n_patched_from = sizeof(af_packet_patched_branches) / + sizeof(af_packet_patched_branches[0]), +}; + +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 af_packet_detect(const struct iamroot_ctx *ctx) +{ + struct kernel_version v; + if (!kernel_version_current(&v)) { + fprintf(stderr, "[!] af_packet: could not parse kernel version\n"); + return IAMROOT_TEST_ERROR; + } + + bool patched = kernel_range_is_patched(&af_packet_range, &v); + if (patched) { + if (!ctx->json) { + fprintf(stderr, "[+] af_packet: kernel %s is patched\n", v.release); + } + return IAMROOT_OK; + } + + int userns_ok = can_unshare_userns(); + if (!ctx->json) { + fprintf(stderr, "[i] af_packet: kernel %s in vulnerable range\n", v.release); + fprintf(stderr, "[i] af_packet: user_ns+net_ns clone (CAP_NET_RAW gate): %s\n", + userns_ok == 1 ? "ALLOWED" : + userns_ok == 0 ? "DENIED" : "could not test"); + } + + if (userns_ok == 0) { + if (!ctx->json) { + fprintf(stderr, "[+] af_packet: user_ns denied → " + "unprivileged exploit unreachable\n"); + } + return IAMROOT_PRECOND_FAIL; + } + if (!ctx->json) { + fprintf(stderr, "[!] af_packet: VULNERABLE — kernel in range AND user_ns reachable\n"); + } + return IAMROOT_VULNERABLE; +} + +static iamroot_result_t af_packet_exploit(const struct iamroot_ctx *ctx) +{ + (void)ctx; + fprintf(stderr, + "[-] af_packet: exploit not yet implemented in IAMROOT.\n" + " Status: 🔵 DETECT-ONLY. Reference: Konovalov's PoC.\n" + " Exploit shape: unshare userns → setsockopt(SOL_PACKET,\n" + " PACKET_VERSION, TPACKET_V3) → setsockopt with crafted\n" + " tpacket_req3 (tp_block_size + tp_frame_size triggers overflow)\n" + " → heap write-where → cred overwrite.\n"); + return IAMROOT_PRECOND_FAIL; +} + +static const char af_packet_auditd[] = + "# AF_PACKET TPACKET_V3 LPE (CVE-2017-7308) — auditd detection rules\n" + "# Flag AF_PACKET socket creation from non-root via userns.\n" + "-a always,exit -F arch=b64 -S socket -F a0=17 -k iamroot-af-packet\n" + "-a always,exit -F arch=b64 -S unshare -k iamroot-af-packet-userns\n"; + +const struct iamroot_module af_packet_module = { + .name = "af_packet", + .cve = "CVE-2017-7308", + .summary = "AF_PACKET TPACKET_V3 integer overflow → heap write-where → cred overwrite", + .family = "af_packet", + .kernel_range = "K < 4.10.6, backports: 4.10.6 / 4.9.18 / 4.4.57 / 3.18.49", + .detect = af_packet_detect, + .exploit = af_packet_exploit, + .mitigate = NULL, + .cleanup = NULL, + .detect_auditd = af_packet_auditd, + .detect_sigma = NULL, + .detect_yara = NULL, + .detect_falco = NULL, +}; + +void iamroot_register_af_packet(void) +{ + iamroot_register(&af_packet_module); +} diff --git a/modules/af_packet_cve_2017_7308/iamroot_modules.h b/modules/af_packet_cve_2017_7308/iamroot_modules.h new file mode 100644 index 0000000..055974b --- /dev/null +++ b/modules/af_packet_cve_2017_7308/iamroot_modules.h @@ -0,0 +1,12 @@ +/* + * af_packet_cve_2017_7308 — IAMROOT module registry hook + */ + +#ifndef AF_PACKET_IAMROOT_MODULES_H +#define AF_PACKET_IAMROOT_MODULES_H + +#include "../../core/module.h" + +extern const struct iamroot_module af_packet_module; + +#endif diff --git a/modules/fuse_legacy_cve_2022_0185/iamroot_modules.c b/modules/fuse_legacy_cve_2022_0185/iamroot_modules.c new file mode 100644 index 0000000..e502180 --- /dev/null +++ b/modules/fuse_legacy_cve_2022_0185/iamroot_modules.c @@ -0,0 +1,163 @@ +/* + * fuse_legacy_cve_2022_0185 — IAMROOT module + * + * legacy_parse_param() in fs/fs_context.c had a heap overflow when + * parsing the "fsconfig" filesystem option strings — specifically, + * legacy_load_simple_buf() didn't bound-check the option length. + * Originally reported as a FUSE mount path bug but actually applies + * to any filesystem mountable from a userns (FUSE was just the + * easiest reach). + * + * Discovered by William Liu / Crusaders of Rust (Jan 2022). Famous + * in container-escape contexts (docker/k8s, especially rootless). + * + * STATUS: 🔵 DETECT-ONLY. Public PoC by William Liu (gh repo + * Crusaders-of-Rust/CVE-2022-0185) demonstrates kernel R/W + cred + * overwrite via cross-cache UAF; porting is a follow-up. + * + * Affected: kernel 5.1+ until fix: + * Mainline fix: 722d94847de29 (Jan 18 2022) — lands in 5.16.2 + * 5.16.x : K >= 5.16.2 + * 5.15.x : K >= 5.15.14 + * 5.10.x : K >= 5.10.91 + * 5.4.x : K >= 5.4.171 + * + * Preconditions: + * - Unprivileged user_ns + mount-ns (to get CAP_SYS_ADMIN inside userns) + * - Any mountable filesystem from userns context (legacy_load path + * used FUSE, but cgroup2 and others also reach the bug) + * + * For "tool for system admins": this is the container-escape angle. + * Workloads running rootless containers (Podman, snap, flatpak) sit + * on this bug if the host kernel is unpatched and unprivileged_userns + * is enabled. + */ + +#include "iamroot_modules.h" +#include "../../core/registry.h" +#include "../../core/kernel_range.h" + +#include +#include +#include +#include +#include +#include + +static const struct kernel_patched_from fuse_legacy_patched_branches[] = { + {5, 4, 171}, + {5, 10, 91}, + {5, 15, 14}, + {5, 16, 2}, + {5, 17, 0}, /* mainline */ +}; + +static const struct kernel_range fuse_legacy_range = { + .patched_from = fuse_legacy_patched_branches, + .n_patched_from = sizeof(fuse_legacy_patched_branches) / + sizeof(fuse_legacy_patched_branches[0]), +}; + +static int can_unshare_userns_mount(void) +{ + pid_t pid = fork(); + if (pid < 0) return -1; + if (pid == 0) { + if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) _exit(0); + _exit(1); + } + int status; + waitpid(pid, &status, 0); + return WIFEXITED(status) && WEXITSTATUS(status) == 0; +} + +static iamroot_result_t fuse_legacy_detect(const struct iamroot_ctx *ctx) +{ + struct kernel_version v; + if (!kernel_version_current(&v)) { + fprintf(stderr, "[!] fuse_legacy: could not parse kernel version\n"); + return IAMROOT_TEST_ERROR; + } + + /* Bug introduced in 5.1 (when legacy_parse_param landed). Pre-5.1 + * kernels predate the code path entirely. */ + if (v.major < 5 || (v.major == 5 && v.minor < 1)) { + if (!ctx->json) { + fprintf(stderr, "[+] fuse_legacy: kernel %s predates the bug introduction\n", + v.release); + } + return IAMROOT_OK; + } + + bool patched = kernel_range_is_patched(&fuse_legacy_range, &v); + if (patched) { + if (!ctx->json) { + fprintf(stderr, "[+] fuse_legacy: kernel %s is patched\n", v.release); + } + return IAMROOT_OK; + } + + int userns_ok = can_unshare_userns_mount(); + if (!ctx->json) { + fprintf(stderr, "[i] fuse_legacy: kernel %s in vulnerable range\n", v.release); + fprintf(stderr, "[i] fuse_legacy: user_ns+mount_ns clone (CAP_SYS_ADMIN gate): %s\n", + userns_ok == 1 ? "ALLOWED" : + userns_ok == 0 ? "DENIED" : "could not test"); + } + + if (userns_ok == 0) { + if (!ctx->json) { + fprintf(stderr, "[+] fuse_legacy: user_ns denied → " + "unprivileged exploit unreachable\n"); + } + return IAMROOT_PRECOND_FAIL; + } + if (!ctx->json) { + fprintf(stderr, "[!] fuse_legacy: VULNERABLE — kernel in range AND " + "userns+mountns reachable\n"); + fprintf(stderr, "[i] fuse_legacy: container-escape relevant for rootless " + "docker/podman/snap setups\n"); + } + return IAMROOT_VULNERABLE; +} + +static iamroot_result_t fuse_legacy_exploit(const struct iamroot_ctx *ctx) +{ + (void)ctx; + fprintf(stderr, + "[-] fuse_legacy: exploit not yet implemented in IAMROOT.\n" + " Status: 🔵 DETECT-ONLY. Reference: William Liu's PoC\n" + " (github.com/Crusaders-of-Rust/CVE-2022-0185). Exploit\n" + " shape: unshare userns+mountns → fsopen('cgroup2') →\n" + " fsconfig with crafted long option string → heap OOB write\n" + " → msg_msg cross-cache groom → kernel R/W → cred overwrite.\n"); + return IAMROOT_PRECOND_FAIL; +} + +static const char fuse_legacy_auditd[] = + "# CVE-2022-0185 — auditd detection rules\n" + "# Flag unshare(USER|NS) chained with fsopen/fsconfig from non-root.\n" + "-a always,exit -F arch=b64 -S unshare -k iamroot-fuse-legacy\n" + "-a always,exit -F arch=b64 -S fsopen -k iamroot-fuse-legacy-fsopen\n" + "-a always,exit -F arch=b64 -S fsconfig -k iamroot-fuse-legacy-fsconfig\n"; + +const struct iamroot_module fuse_legacy_module = { + .name = "fuse_legacy", + .cve = "CVE-2022-0185", + .summary = "legacy_parse_param fsconfig heap OOB → container-escape LPE", + .family = "fuse_legacy", + .kernel_range = "5.1 ≤ K, fixed mainline 5.16.2; backports: 5.16.2 / 5.15.14 / 5.10.91 / 5.4.171", + .detect = fuse_legacy_detect, + .exploit = fuse_legacy_exploit, + .mitigate = NULL, + .cleanup = NULL, + .detect_auditd = fuse_legacy_auditd, + .detect_sigma = NULL, + .detect_yara = NULL, + .detect_falco = NULL, +}; + +void iamroot_register_fuse_legacy(void) +{ + iamroot_register(&fuse_legacy_module); +} diff --git a/modules/fuse_legacy_cve_2022_0185/iamroot_modules.h b/modules/fuse_legacy_cve_2022_0185/iamroot_modules.h new file mode 100644 index 0000000..8844db4 --- /dev/null +++ b/modules/fuse_legacy_cve_2022_0185/iamroot_modules.h @@ -0,0 +1,12 @@ +/* + * fuse_legacy_cve_2022_0185 — IAMROOT module registry hook + */ + +#ifndef FUSE_LEGACY_IAMROOT_MODULES_H +#define FUSE_LEGACY_IAMROOT_MODULES_H + +#include "../../core/module.h" + +extern const struct iamroot_module fuse_legacy_module; + +#endif