Phase 7: af_packet (CVE-2017-7308) + FUSE legacy (CVE-2022-0185)
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.
This commit is contained in:
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user