diff --git a/CVES.md b/CVES.md index ef2e4c4..b4d07fd 100644 --- a/CVES.md +++ b/CVES.md @@ -31,6 +31,8 @@ Status legend: | 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-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-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 c74601e..f6a0c7f 100644 --- a/Makefile +++ b/Makefile @@ -66,10 +66,20 @@ DCOW_DIR := modules/dirty_cow_cve_2016_5195 DCOW_SRCS := $(DCOW_DIR)/iamroot_modules.c DCOW_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(DCOW_SRCS)) +# Family: ptrace_traceme (CVE-2019-13272) +PTM_DIR := modules/ptrace_traceme_cve_2019_13272 +PTM_SRCS := $(PTM_DIR)/iamroot_modules.c +PTM_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(PTM_SRCS)) + +# Family: netfilter_xtcompat (CVE-2021-22555) +NXC_DIR := modules/netfilter_xtcompat_cve_2021_22555 +NXC_SRCS := $(NXC_DIR)/iamroot_modules.c +NXC_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(NXC_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) +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) .PHONY: all clean debug static help diff --git a/core/registry.h b/core/registry.h index 180c906..4f9ae3d 100644 --- a/core/registry.h +++ b/core/registry.h @@ -28,5 +28,7 @@ void iamroot_register_nf_tables(void); void iamroot_register_overlayfs(void); void iamroot_register_cls_route4(void); void iamroot_register_dirty_cow(void); +void iamroot_register_ptrace_traceme(void); +void iamroot_register_netfilter_xtcompat(void); #endif /* IAMROOT_REGISTRY_H */ diff --git a/iamroot.c b/iamroot.c index 0702a82..e195f48 100644 --- a/iamroot.c +++ b/iamroot.c @@ -347,6 +347,8 @@ int main(int argc, char **argv) iamroot_register_overlayfs(); iamroot_register_cls_route4(); iamroot_register_dirty_cow(); + iamroot_register_ptrace_traceme(); + iamroot_register_netfilter_xtcompat(); enum mode mode = MODE_SCAN; struct iamroot_ctx ctx = {0}; diff --git a/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.c b/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.c new file mode 100644 index 0000000..681b96e --- /dev/null +++ b/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.c @@ -0,0 +1,161 @@ +/* + * netfilter_xtcompat_cve_2021_22555 — IAMROOT module + * + * Heap-out-of-bounds in xt_compat_target_to_user(): the 32-bit + * compat handler for iptables rule export wrote up to 4 bytes + * beyond a heap allocation when copying rule names from kernel to + * userspace. Exploitable via msg_msg slab cross-cache groom into + * a kernel R/W primitive. + * + * Discovered by Andy Nguyen (Google), April 2021. Famous because + * the bug existed since 2.6.19 (2006) — fifteen years of latent + * vulnerability — and it works on default-config kernels with + * unprivileged user_ns enabled (no special hardware or modules). + * + * STATUS: 🔵 DETECT-ONLY. Public PoC (Andy's "exploit.c") works + * end-to-end with msg_msg + sk_buff sprays; porting is ~400 lines. + * + * Affected: kernel 2.6.19+ until backports landed: + * 5.11.x : K >= 5.11.10 + * 5.10.x : K >= 5.10.27 + * 5.4.x : K >= 5.4.110 + * 4.19.x : K >= 4.19.185 + * 4.14.x : K >= 4.14.230 + * 4.9.x : K >= 4.9.266 + * 4.4.x : K >= 4.4.266 + * + * Preconditions: + * - CAP_NET_ADMIN (usually via unprivileged user_ns clone) + * - iptables/ip_tables/x_tables kernel modules available + * (almost always autoload-able on default-config kernels) + */ + +#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 netfilter_xtcompat_patched_branches[] = { + {4, 4, 266}, + {4, 9, 266}, + {4, 14, 230}, + {4, 19, 185}, + {5, 4, 110}, + {5, 10, 27}, + {5, 11, 10}, + {5, 12, 0}, /* mainline (5.12-rc) */ +}; + +static const struct kernel_range netfilter_xtcompat_range = { + .patched_from = netfilter_xtcompat_patched_branches, + .n_patched_from = sizeof(netfilter_xtcompat_patched_branches) / + sizeof(netfilter_xtcompat_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 netfilter_xtcompat_detect(const struct iamroot_ctx *ctx) +{ + struct kernel_version v; + if (!kernel_version_current(&v)) { + fprintf(stderr, "[!] netfilter_xtcompat: could not parse kernel version\n"); + return IAMROOT_TEST_ERROR; + } + + if (v.major < 2 || (v.major == 2 && v.minor < 6)) { + if (!ctx->json) { + fprintf(stderr, "[+] netfilter_xtcompat: kernel %s predates the bug introduction\n", + v.release); + } + return IAMROOT_OK; + } + + bool patched = kernel_range_is_patched(&netfilter_xtcompat_range, &v); + if (patched) { + if (!ctx->json) { + fprintf(stderr, "[+] netfilter_xtcompat: kernel %s is patched\n", v.release); + } + return IAMROOT_OK; + } + + int userns_ok = can_unshare_userns(); + if (!ctx->json) { + fprintf(stderr, "[i] netfilter_xtcompat: kernel %s in vulnerable range " + "(bug existed since 2.6.19, 2006)\n", v.release); + fprintf(stderr, "[i] netfilter_xtcompat: user_ns+net_ns clone: %s\n", + userns_ok == 1 ? "ALLOWED" : + userns_ok == 0 ? "DENIED" : "could not test"); + } + + if (userns_ok == 0) { + if (!ctx->json) { + fprintf(stderr, "[+] netfilter_xtcompat: user_ns denied → " + "unprivileged exploit path unreachable\n"); + } + return IAMROOT_PRECOND_FAIL; + } + + if (!ctx->json) { + fprintf(stderr, "[!] netfilter_xtcompat: VULNERABLE — kernel in range " + "AND user_ns reachable\n"); + } + return IAMROOT_VULNERABLE; +} + +static iamroot_result_t netfilter_xtcompat_exploit(const struct iamroot_ctx *ctx) +{ + (void)ctx; + fprintf(stderr, + "[-] netfilter_xtcompat: exploit not yet implemented in IAMROOT.\n" + " Status: 🔵 DETECT-ONLY. Reference: Andy Nguyen's public PoC\n" + " (~400 lines, msg_msg + sk_buff cross-cache groom). Porting\n" + " is a substantial follow-up — the exploit's heap-massage\n" + " sequence and cred-overwrite walk are the bulk.\n"); + return IAMROOT_PRECOND_FAIL; +} + +static const char netfilter_xtcompat_auditd[] = + "# CVE-2021-22555 — auditd detection rules\n" + "# The exploit's hallmarks: unshare(USER|NET) chained with iptables\n" + "# rule setup via setsockopt() and msgsnd/msgrcv heap-spray patterns.\n" + "-a always,exit -F arch=b64 -S unshare -k iamroot-xtcompat\n" + "-a always,exit -F arch=b64 -S setsockopt -F a2=64 -k iamroot-xtcompat-iptopt\n" + "-a always,exit -F arch=b64 -S msgsnd -k iamroot-xtcompat-msgmsg\n"; + +const struct iamroot_module netfilter_xtcompat_module = { + .name = "netfilter_xtcompat", + .cve = "CVE-2021-22555", + .summary = "iptables xt_compat_target_to_user heap-OOB write → cross-cache UAF → root", + .family = "netfilter_xtcompat", + .kernel_range = "2.6.19 ≤ K, fixed mainline 5.12; backports: 5.11.10 / 5.10.27 / 5.4.110 / 4.19.185 / 4.14.230 / 4.9.266 / 4.4.266", + .detect = netfilter_xtcompat_detect, + .exploit = netfilter_xtcompat_exploit, + .mitigate = NULL, /* mitigation: upgrade kernel; disable unprivileged_userns_clone */ + .cleanup = NULL, + .detect_auditd = netfilter_xtcompat_auditd, + .detect_sigma = NULL, + .detect_yara = NULL, + .detect_falco = NULL, +}; + +void iamroot_register_netfilter_xtcompat(void) +{ + iamroot_register(&netfilter_xtcompat_module); +} diff --git a/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.h b/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.h new file mode 100644 index 0000000..63ee543 --- /dev/null +++ b/modules/netfilter_xtcompat_cve_2021_22555/iamroot_modules.h @@ -0,0 +1,12 @@ +/* + * netfilter_xtcompat_cve_2021_22555 — IAMROOT module registry hook + */ + +#ifndef NETFILTER_XTCOMPAT_IAMROOT_MODULES_H +#define NETFILTER_XTCOMPAT_IAMROOT_MODULES_H + +#include "../../core/module.h" + +extern const struct iamroot_module netfilter_xtcompat_module; + +#endif diff --git a/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.c b/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.c new file mode 100644 index 0000000..46d4b55 --- /dev/null +++ b/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.c @@ -0,0 +1,127 @@ +/* + * ptrace_traceme_cve_2019_13272 — IAMROOT module + * + * PTRACE_TRACEME on a parent that subsequently execve's a setuid + * binary results in the kernel granting ptrace privileges over the + * privileged process to the unprivileged child. Discovered by Jann + * Horn (Google Project Zero, June 2019). + * + * STATUS: 🔵 DETECT-ONLY. Exploit follows jannh's public PoC: fork + * a child that does PTRACE_TRACEME pointing at the parent, parent + * execve's a chosen setuid binary (e.g., su, pkexec), child then + * ptrace-injects shellcode into the now-elevated process. + * + * Affected: kernels < 5.1.17 mainline. Stable backports varied; the + * fix landed in stable as: + * 5.1.x : K >= 5.1.17 + * 5.0.x : K >= 5.0.20 (older LTS — many distros stayed on 4.x) + * 4.19.x: K >= 4.19.58 + * 4.14.x: K >= 4.14.131 + * 4.9.x : K >= 4.9.182 + * 4.4.x : K >= 4.4.182 + * + * No exotic preconditions. Doesn't need user_ns. Works on + * default-config systems — that's part of why it's famous: even + * locked-down environments without unprivileged_userns_clone were + * vulnerable. + */ + +#include "iamroot_modules.h" +#include "../../core/registry.h" +#include "../../core/kernel_range.h" + +#include +#include +#include +#include + +static const struct kernel_patched_from ptrace_traceme_patched_branches[] = { + {4, 4, 182}, + {4, 9, 182}, + {4, 14, 131}, + {4, 19, 58}, + {5, 0, 20}, + {5, 1, 17}, + {5, 2, 0}, /* mainline (5.2-rc) */ +}; + +static const struct kernel_range ptrace_traceme_range = { + .patched_from = ptrace_traceme_patched_branches, + .n_patched_from = sizeof(ptrace_traceme_patched_branches) / + sizeof(ptrace_traceme_patched_branches[0]), +}; + +static iamroot_result_t ptrace_traceme_detect(const struct iamroot_ctx *ctx) +{ + struct kernel_version v; + if (!kernel_version_current(&v)) { + fprintf(stderr, "[!] ptrace_traceme: could not parse kernel version\n"); + return IAMROOT_TEST_ERROR; + } + + /* Bug existed since ptrace's inception (early 2.x); anything + * pre-LTS-backport is vulnerable. Anything < 4.4 in our range + * model defaults to vulnerable since no entry covers it. */ + if (v.major < 4 || (v.major == 4 && v.minor < 4)) { + if (!ctx->json) { + fprintf(stderr, "[!] ptrace_traceme: ancient kernel %s — assume VULNERABLE\n", + v.release); + } + return IAMROOT_VULNERABLE; + } + + bool patched = kernel_range_is_patched(&ptrace_traceme_range, &v); + if (patched) { + if (!ctx->json) { + fprintf(stderr, "[+] ptrace_traceme: kernel %s is patched\n", v.release); + } + return IAMROOT_OK; + } + if (!ctx->json) { + fprintf(stderr, "[!] ptrace_traceme: kernel %s in vulnerable range\n", v.release); + fprintf(stderr, "[i] ptrace_traceme: no exotic preconditions — works on default config " + "(no user_ns required)\n"); + } + return IAMROOT_VULNERABLE; +} + +static iamroot_result_t ptrace_traceme_exploit(const struct iamroot_ctx *ctx) +{ + (void)ctx; + fprintf(stderr, + "[-] ptrace_traceme: exploit not yet implemented in IAMROOT.\n" + " Status: 🔵 DETECT-ONLY. Reference: jannh's PoC.\n" + " Exploit shape: fork() → child calls PTRACE_TRACEME → parent\n" + " execve's a setuid binary (su, pkexec, ping with cap_net_raw,\n" + " etc.) → child becomes tracer of the now-privileged process\n" + " → ptrace-inject shellcode → root.\n"); + return IAMROOT_PRECOND_FAIL; +} + +static const char ptrace_traceme_auditd[] = + "# PTRACE_TRACEME LPE (CVE-2019-13272) — auditd detection rules\n" + "# Flag PTRACE_TRACEME (request 0) followed by parent execve of\n" + "# a setuid binary. False positives: gdb, strace, debuggers.\n" + "-a always,exit -F arch=b64 -S ptrace -F a0=0 -k iamroot-ptrace-traceme\n" + "-a always,exit -F arch=b32 -S ptrace -F a0=0 -k iamroot-ptrace-traceme\n"; + +const struct iamroot_module ptrace_traceme_module = { + .name = "ptrace_traceme", + .cve = "CVE-2019-13272", + .summary = "PTRACE_TRACEME → setuid binary execve → cred-escalation via ptrace inject", + .family = "ptrace_traceme", + .kernel_range = "K < 5.1.17, backports: 5.0.20 / 4.19.58 / 4.14.131 / 4.9.182 / 4.4.182", + .detect = ptrace_traceme_detect, + .exploit = ptrace_traceme_exploit, + .mitigate = NULL, /* mitigation: upgrade kernel; OR set ptrace_scope sysctl */ + .cleanup = NULL, + .detect_auditd = ptrace_traceme_auditd, + .detect_sigma = NULL, + .detect_yara = NULL, + .detect_falco = NULL, +}; + +void iamroot_register_ptrace_traceme(void) +{ + iamroot_register(&ptrace_traceme_module); +} diff --git a/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.h b/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.h new file mode 100644 index 0000000..1849039 --- /dev/null +++ b/modules/ptrace_traceme_cve_2019_13272/iamroot_modules.h @@ -0,0 +1,12 @@ +/* + * ptrace_traceme_cve_2019_13272 — IAMROOT module registry hook + */ + +#ifndef PTRACE_TRACEME_IAMROOT_MODULES_H +#define PTRACE_TRACEME_IAMROOT_MODULES_H + +#include "../../core/module.h" + +extern const struct iamroot_module ptrace_traceme_module; + +#endif