Phase 7: overlayfs CVE-2021-3493 module (Ubuntu userns LPE) — detect-only

10th module. Ubuntu-specific userns + overlayfs LPE that injects file
capabilities cross-namespace.

- modules/overlayfs_cve_2021_3493/iamroot_modules.{c,h}:
  - is_ubuntu() — parses /etc/os-release for ID=ubuntu or
    ID_LIKE=ubuntu. Non-Ubuntu hosts get IAMROOT_OK immediately (the
    bug is specific to Ubuntu's modified overlayfs).
  - unprivileged_userns_clone gate — sysctl=0 → PRECOND_FAIL
  - Active probe (--active): forks a child that enters userns +
    mountns and attempts the overlayfs mount inside /tmp. Mount
    success on Ubuntu = VULNERABLE. Mount denied = patched / AppArmor
    block. Child-isolated so parent's namespace state is untouched.
  - Version fallback: kernel < 5.13 = vulnerable-by-inference for
    Ubuntu kernels; recommend --active for confirmation.
  - Exploit: detect-only stub. Reference vsh's exploit-cve-2021-3493
    for full version (mount overlayfs in userns, drop binary with
    cap_setuid+ep into upper layer, re-exec outside ns).
  - Embedded auditd rules: mount(overlay) syscall + security.capability
    xattr writes (the exploit's two-step footprint).

Verified end-to-end on kctf-mgr (Debian):
  iamroot --scan → 'not Ubuntu — bug is Ubuntu-specific' → IAMROOT_OK

Module count: 10. Active-probe pattern now applies to dirty_pipe,
entrybleed, and overlayfs (and copy_fail_family via existing
dirtyfail_active_probes global). Detect quality across the corpus
materially improved this session.
This commit is contained in:
2026-05-16 20:22:32 -04:00
parent b206610a8e
commit 3eeee01f06
6 changed files with 259 additions and 1 deletions
+1
View File
@@ -28,6 +28,7 @@ Status legend:
| CVE-2026-31402 | NFS replay-cache heap overflow | LPE (NFS server) | mainline 2026-04-03 | — | ⚪ | Candidate. Different audience (NFS servers) — TBD whether in-scope. | | CVE-2026-31402 | NFS replay-cache heap overflow | LPE (NFS server) | mainline 2026-04-03 | — | ⚪ | Candidate. Different audience (NFS servers) — TBD whether in-scope. |
| 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-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
@@ -51,10 +51,15 @@ NFT_DIR := modules/nf_tables_cve_2024_1086
NFT_SRCS := $(NFT_DIR)/iamroot_modules.c NFT_SRCS := $(NFT_DIR)/iamroot_modules.c
NFT_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(NFT_SRCS)) NFT_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(NFT_SRCS))
# Family: overlayfs (CVE-2021-3493)
OVL_DIR := modules/overlayfs_cve_2021_3493
OVL_SRCS := $(OVL_DIR)/iamroot_modules.c
OVL_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(OVL_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) ALL_OBJS := $(TOP_OBJ) $(CORE_OBJS) $(CFF_OBJS) $(DP_OBJS) $(EB_OBJS) $(PK_OBJS) $(NFT_OBJS) $(OVL_OBJS)
.PHONY: all clean debug static help .PHONY: all clean debug static help
+1
View File
@@ -25,5 +25,6 @@ void iamroot_register_dirty_pipe(void);
void iamroot_register_entrybleed(void); 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);
#endif /* IAMROOT_REGISTRY_H */ #endif /* IAMROOT_REGISTRY_H */
+1
View File
@@ -222,6 +222,7 @@ int main(int argc, char **argv)
iamroot_register_entrybleed(); iamroot_register_entrybleed();
iamroot_register_pwnkit(); iamroot_register_pwnkit();
iamroot_register_nf_tables(); iamroot_register_nf_tables();
iamroot_register_overlayfs();
enum mode mode = MODE_SCAN; enum mode mode = MODE_SCAN;
struct iamroot_ctx ctx = {0}; struct iamroot_ctx ctx = {0};
@@ -0,0 +1,238 @@
/*
* overlayfs_cve_2021_3493 — IAMROOT module
*
* Ubuntu-flavor overlayfs lets an unprivileged user mount overlayfs
* inside a user namespace, then set file capabilities on a file in
* the upper layer. The capabilities are NOT scoped to the userns —
* they propagate to the host view of the same inode, letting an
* unprivileged user create a file with CAP_SETUID/CAP_DAC_OVERRIDE
* that root will honor outside the namespace.
*
* Discovered by Vasily Kulikov; published April 2021. Specific to
* Ubuntu's modified overlayfs (vanilla upstream overlayfs didn't
* allow the userns-mount path at the time). The fix landed via
* Ubuntu's apparmor + an upstream Vfsmount audit.
*
* STATUS: 🔵 DETECT-ONLY. Exploit is well-documented (vsh's
* exploit-cve-2021-3493) and would port in ~80 lines; follow-up
* commit lands it.
*
* Affected:
* - Ubuntu 14.04 / 16.04 / 18.04 / 20.04 / 20.10 / 21.04 with
* Ubuntu-modified overlayfs and unprivileged_userns_clone=1.
* - Upstream kernels did NOT have the userns-mount path enabled
* pre-5.11, so non-Ubuntu kernels were largely immune by
* accident.
* - Fixed in Ubuntu by USN-4915-1 (April 2021) — kernel package
* updates per release.
*
* Detect logic (necessary-but-not-sufficient):
* 1. /etc/os-release distro == ubuntu (the bug is Ubuntu-specific)
* 2. Kernel version is below the Ubuntu fix threshold for that
* release. We don't track per-release Ubuntu kernel version
* maps in IAMROOT yet; report VULNERABLE if Ubuntu kernel
* AND uname() version < 5.11 AND unprivileged_userns_clone=1
* AND overlayfs mountable from userns (active probe).
*/
#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 <fcntl.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
static bool is_ubuntu(void)
{
FILE *f = fopen("/etc/os-release", "r");
if (!f) return false;
char line[256];
bool found = false;
while (fgets(line, sizeof line, f)) {
if (strstr(line, "ID=ubuntu") || strstr(line, "ID_LIKE=ubuntu")) {
found = true;
break;
}
}
fclose(f);
return found;
}
static int read_sysctl_int(const char *path)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
char buf[16] = {0};
ssize_t n = read(fd, buf, sizeof buf - 1);
close(fd);
if (n <= 0) return -1;
return atoi(buf);
}
/* Active probe: actually try to mount overlayfs inside a user
* namespace. The probe is contained: forks a child that enters a
* userns and attempts the mount; child exits regardless. Parent
* never enters the namespace.
*
* Returns 1 if mount succeeded (vulnerable behavior), 0 if denied
* (AppArmor / SELinux / kernel patch), -1 on probe machinery error. */
static int overlayfs_mount_probe(void)
{
pid_t pid = fork();
if (pid < 0) return -1;
if (pid == 0) {
if (unshare(CLONE_NEWUSER | CLONE_NEWNS) < 0) _exit(2);
/* Build a minimal overlayfs in /tmp inside the child. */
char base[] = "/tmp/iamroot-ovl-XXXXXX";
if (!mkdtemp(base)) _exit(3);
char low[512], up[512], wd[512], mp[512];
snprintf(low, sizeof low, "%s/lower", base);
snprintf(up, sizeof up, "%s/upper", base);
snprintf(wd, sizeof wd, "%s/work", base);
snprintf(mp, sizeof mp, "%s/merged", base);
if (mkdir(low, 0755) < 0 || mkdir(up, 0755) < 0
|| mkdir(wd, 0755) < 0 || mkdir(mp, 0755) < 0) _exit(4);
char opts[2048];
snprintf(opts, sizeof opts, "lowerdir=%s,upperdir=%s,workdir=%s",
low, up, wd);
int rc = mount("overlay", mp, "overlay", 0, opts);
if (rc < 0) _exit(5); /* mount denied — likely patched/blocked */
umount(mp); /* clean up if we got here */
_exit(0);
}
int status;
waitpid(pid, &status, 0);
if (!WIFEXITED(status)) return -1;
return WEXITSTATUS(status) == 0 ? 1 : 0;
}
static iamroot_result_t overlayfs_detect(const struct iamroot_ctx *ctx)
{
struct kernel_version v;
if (!kernel_version_current(&v)) {
fprintf(stderr, "[!] overlayfs: could not parse kernel version\n");
return IAMROOT_TEST_ERROR;
}
/* Ubuntu-specific bug. Non-Ubuntu kernels are largely immune
* because upstream didn't enable the userns-mount path until
* 5.11. Bail early for non-Ubuntu. */
if (!is_ubuntu()) {
if (!ctx->json) {
fprintf(stderr, "[+] overlayfs: not Ubuntu — bug is Ubuntu-specific\n");
}
return IAMROOT_OK;
}
/* unprivileged_userns_clone gate */
int uuc = read_sysctl_int("/proc/sys/kernel/unprivileged_userns_clone");
if (uuc == 0) {
if (!ctx->json) {
fprintf(stderr, "[+] overlayfs: unprivileged_userns_clone=0 → "
"unprivileged exploit unreachable\n");
}
return IAMROOT_PRECOND_FAIL;
}
if (!ctx->json) {
fprintf(stderr, "[i] overlayfs: Ubuntu kernel %s, unprivileged_userns_clone=%d\n",
v.release, uuc);
}
/* Active probe: try the mount. Most reliable detect since Ubuntu
* kernel package versioning is opaque to us. */
if (ctx->active_probe) {
int probe = overlayfs_mount_probe();
if (probe == 1) {
if (!ctx->json) {
fprintf(stderr, "[!] overlayfs: ACTIVE PROBE CONFIRMED — "
"userns overlayfs mount succeeded → VULNERABLE\n");
}
return IAMROOT_VULNERABLE;
}
if (probe == 0) {
if (!ctx->json) {
fprintf(stderr, "[+] overlayfs: active probe denied mount — "
"likely patched / AppArmor block\n");
}
return IAMROOT_OK;
}
if (!ctx->json) {
fprintf(stderr, "[?] overlayfs: active probe machinery failed\n");
}
}
/* Without active probe, fall back to version inference. Upstream
* 5.11 enabled userns-mount for overlayfs; Ubuntu had it earlier.
* Ubuntu fix is per-release-specific; conservatively report
* VULNERABLE if version < 5.13 (covers most affected Ubuntu LTS),
* and recommend --active for confirmation. */
if (v.major < 5 || (v.major == 5 && v.minor < 13)) {
if (!ctx->json) {
fprintf(stderr, "[!] overlayfs: Ubuntu kernel %s in vulnerable range — "
"re-run with --active to confirm\n", v.release);
}
return IAMROOT_VULNERABLE;
}
if (!ctx->json) {
fprintf(stderr, "[+] overlayfs: Ubuntu kernel %s is newer than typical "
"affected range\n", v.release);
fprintf(stderr, "[i] overlayfs: re-run with --active to empirically test\n");
}
return IAMROOT_OK;
}
static iamroot_result_t overlayfs_exploit(const struct iamroot_ctx *ctx)
{
(void)ctx;
fprintf(stderr,
"[-] overlayfs: exploit not yet implemented in IAMROOT.\n"
" Status: 🔵 DETECT-ONLY (see CVES.md).\n"
" Reference: vsh's exploit-cve-2021-3493. The exploit mounts\n"
" overlayfs inside a userns, places a /bin/sh-like binary in\n"
" the upper layer with cap_setuid+ep set, then re-executes it\n"
" outside the namespace to drop a root shell.\n");
return IAMROOT_PRECOND_FAIL;
}
/* ----- Embedded detection rules ----- */
static const char overlayfs_auditd[] =
"# overlayfs userns LPE (CVE-2021-3493) — auditd detection rules\n"
"# Flag userns-clone followed by overlayfs mount + setcap-like xattr.\n"
"-a always,exit -F arch=b64 -S mount -F a2=overlay -k iamroot-overlayfs\n"
"-a always,exit -F arch=b32 -S mount -F a2=overlay -k iamroot-overlayfs\n"
"# Watch for security.capability xattr writes (the post-mount step)\n"
"-a always,exit -F arch=b64 -S setxattr,fsetxattr,lsetxattr -k iamroot-overlayfs-cap\n";
const struct iamroot_module overlayfs_module = {
.name = "overlayfs",
.cve = "CVE-2021-3493",
.summary = "Ubuntu userns-overlayfs file-capability injection → host root",
.family = "overlayfs",
.kernel_range = "Ubuntu-specific; kernels w/ userns-overlayfs-mount before per-release fix (USN-4915-1)",
.detect = overlayfs_detect,
.exploit = overlayfs_exploit,
.mitigate = NULL,
.cleanup = NULL,
.detect_auditd = overlayfs_auditd,
.detect_sigma = NULL,
.detect_yara = NULL,
.detect_falco = NULL,
};
void iamroot_register_overlayfs(void)
{
iamroot_register(&overlayfs_module);
}
@@ -0,0 +1,12 @@
/*
* overlayfs_cve_2021_3493 — IAMROOT module registry hook
*/
#ifndef OVERLAYFS_IAMROOT_MODULES_H
#define OVERLAYFS_IAMROOT_MODULES_H
#include "../../core/module.h"
extern const struct iamroot_module overlayfs_module;
#endif