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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user