From e457b22c1f3cf870fff09499733c3b69e5a1315c Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Mon, 8 Jun 2026 10:16:24 -0400 Subject: [PATCH] release v0.9.9: install.sh needs no sudo + CVE metadata drift fix install.sh never escalates to sudo. The installer defaulted to /usr/local/bin and fell back to `sudo mv`, prompting for a password on exactly the unprivileged accounts a privilege-escalation tool targets. It now uses /usr/local/bin only when already writable and otherwise installs to a per-user $HOME/.local/bin (honoring XDG_BIN_HOME), no sudo ever. An explicit SKELETONKEY_PREFIX is honored and errors rather than escalating. The documented one-liner prepends ~/.local/bin to PATH so it resolves on a fresh login, and the quickstart drops the misleading sudo from --scan / --audit / --auto. CVE metadata drift (the failing weekly drift-check): - CVE-2022-0492 (cgroup_release_agent) entered CISA KEV 2026-06-02; corpus now 13 of 36 modules cover KEV-listed CVEs. - CVE-2026-46333 (ptrace_pidfd) gained CWE-269 from NVD (was unclassified at module-add time). Refreshed CVE_METADATA.json, generated cve_metadata.c, and KEV_CROSSREF.md; README + website KEV counts and version bumped to 0.9.9. --- CVES.md | 2 +- README.md | 5 +- core/cve_metadata.c | 6 +- docs/CVE_METADATA.json | 6 +- docs/KEV_CROSSREF.md | 6 +- docs/LAUNCH.md | 1 + docs/RELEASE_NOTES.md | 34 ++++++++++++ docs/app.js | 2 +- docs/index.html | 8 +-- install.sh | 52 +++++++++++++----- skeletonkey.c | 2 +- .../refresh-cve-metadata.cpython-314.pyc | Bin 0 -> 20184 bytes 12 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 tools/__pycache__/refresh-cve-metadata.cpython-314.pyc diff --git a/CVES.md b/CVES.md index 079c655..bdca178 100644 --- a/CVES.md +++ b/CVES.md @@ -75,7 +75,7 @@ root on a host can upstream their kernel's offsets via PR. | 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` | 🟑 | Userns+netns reach, tc/ip dummy interface + route4 dangling-filter add/del, msg_msg kmalloc-1k spray, UDP classify drive to follow the dangling pointer, slabinfo delta witness. Stops at empirical UAF-fired signal; no leakβ†’cred overwrite (per-kernel offsets refused). Branch backports: 5.4.213 / 5.10.143 / 5.15.69 / 5.18.18 / 5.19.7. | | 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` | 🟒 | Full detect + exploit. Branch backports: 4.4.182 / 4.9.182 / 4.14.131 / 4.19.58 / 5.0.20 / 5.1.17. jannh-style: fork β†’ child `PTRACE_TRACEME` β†’ child sleep+attach β†’ parent `execve` setuid bin (pkexec/su/passwd auto-selected) β†’ child wins stale-ptrace_link β†’ POKETEXT x86_64 shellcode β†’ root sh. x86_64-only; ARM/other return PRECOND_FAIL cleanly. | -| CVE-2022-0492 | cgroup v1 `release_agent` privilege check in wrong namespace | LPE (host root from rootless container or unprivileged userns) | mainline 5.17 (Mar 2022) | `cgroup_release_agent` | 🟒 | Universal structural exploit β€” no per-kernel offsets, no race. unshare(user|mount|cgroup), mount cgroup v1 RDP controller, write release_agent β†’ ./payload, trigger via notify_on_release. Ships auditd rules covering cgroupfs mount + release_agent writes. Kept as a portable "containers misconfigured" demo. | +| CVE-2022-0492 | cgroup v1 `release_agent` privilege check in wrong namespace | LPE (host root from rootless container or unprivileged userns) | mainline 5.17 (Mar 2022) | `cgroup_release_agent` | 🟒 | Universal structural exploit β€” no per-kernel offsets, no race. unshare(user|mount|cgroup), mount cgroup v1 RDP controller, write release_agent β†’ ./payload, trigger via notify_on_release. Ships auditd rules covering cgroupfs mount + release_agent writes. Kept as a portable "containers misconfigured" demo. **Added to CISA KEV 2026-06-02 β€” now confirmed exploited in the wild.** | | CVE-2023-0386 | overlayfs `copy_up` preserves setuid bit across mount-ns boundary | LPE (host root via setuid carrier from unprivileged mount) | mainline 5.11 / 6.2-rc6 (Jan 2023) | `overlayfs_setuid` | 🟒 | Distro-agnostic β€” places a setuid binary in an overlay lower, mounts via fuse-overlayfs userns trick, executes from upper to inherit the setuid bit + root euid. Branch backports tracked for 5.10.169 / 5.15.92 / 6.1.11 / 6.2.x. | | 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` | 🟑 | Hand-rolled `ipt_replace` blob + setsockopt(IPT_SO_SET_REPLACE) fires the 4-byte OOB, msg_msg spray in kmalloc-2k + sk_buff sidecar, MSG_COPY scan for cross-cache landing + slabinfo delta. Stops before the leak β†’ modprobe_path overwrite chain (per-kernel offsets refused). 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).** Andy Nguyen's PGZ disclosure. | | CVE-2017-7308 | AF_PACKET TPACKET_V3 integer overflow β†’ heap write-where | LPE (CAP_NET_RAW via userns) | mainline 4.11 / 4.10.6 (Mar 2017) | `af_packet` | 🟑 | Konovalov's TPACKET_V3 overflow + 200-skb spray + best-effort cred race. Offset table (Ubuntu 16.04/4.4 + 18.04/4.15) + `SKELETONKEY_AFPACKET_OFFSETS` env override for other kernels. x86_64-only; ARM returns PRECOND_FAIL. Branch backports: 4.10.6 / 4.9.18 / 4.4.57 / 3.18.49. | diff --git a/README.md b/README.md index cd4ff65..d60091e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ```bash curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \ + && export PATH="$HOME/.local/bin:$PATH" \ && skeletonkey --auto --i-know ``` @@ -205,7 +206,7 @@ also compile (modules with Linux-only headers stub out gracefully). ## Status -**v0.9.8 cut 2026-06-02.** 41 modules across 36 CVEs β€” **every +**v0.9.9 cut 2026-06-08.** 41 modules across 36 CVEs β€” **every year 2016 β†’ 2026 now covered**. Newest: `ptrace_pidfd` (CVE-2026-46333, Qualys's `__ptrace_may_access` / `pidfd_getfd` credential-steal) and `sudo_host` (CVE-2025-32462, Stratascale's sudo `--host` policy bypass). @@ -238,7 +239,7 @@ Reliability + accuracy work in v0.7.x: trace, OPSEC footprint, detection-rule coverage, verified-on records. Paste-into-ticket ready. - **CVE metadata pipeline** (`tools/refresh-cve-metadata.py`) β€” fetches - CISA KEV catalog + NVD CWE; 12 of 34 modules cover KEV-listed CVEs. + CISA KEV catalog + NVD CWE; 13 of 36 modules cover KEV-listed CVEs. - **151 detection rules** across auditd / sigma / yara / falco; one command exports the corpus to your SIEM. - `--auto` upgrades: per-detect 15s timeout, fork-isolated detect + diff --git a/core/cve_metadata.c b/core/cve_metadata.c index 61550ae..ab5fc80 100644 --- a/core/cve_metadata.c +++ b/core/cve_metadata.c @@ -121,8 +121,8 @@ const struct cve_metadata cve_metadata_table[] = { .cwe = "CWE-287", .attack_technique = "T1611", .attack_subtechnique = NULL, - .in_kev = false, - .kev_date_added = "", + .in_kev = true, + .kev_date_added = "2026-06-02", }, { .cve = "CVE-2022-0847", @@ -286,7 +286,7 @@ const struct cve_metadata cve_metadata_table[] = { }, { .cve = "CVE-2026-46333", - .cwe = NULL, + .cwe = "CWE-269", .attack_technique = "T1068", .attack_subtechnique = NULL, .in_kev = false, diff --git a/docs/CVE_METADATA.json b/docs/CVE_METADATA.json index 3f36b6a..82583ed 100644 --- a/docs/CVE_METADATA.json +++ b/docs/CVE_METADATA.json @@ -122,8 +122,8 @@ "cwe": "CWE-287", "attack_technique": "T1611", "attack_subtechnique": null, - "in_kev": false, - "kev_date_added": "" + "in_kev": true, + "kev_date_added": "2026-06-02" }, { "cve": "CVE-2022-0847", @@ -308,7 +308,7 @@ { "cve": "CVE-2026-46333", "module_dir": "ptrace_pidfd_cve_2026_46333", - "cwe": null, + "cwe": "CWE-269", "attack_technique": "T1068", "attack_subtechnique": null, "in_kev": false, diff --git a/docs/KEV_CROSSREF.md b/docs/KEV_CROSSREF.md index 777f3d0..4dc688c 100644 --- a/docs/KEV_CROSSREF.md +++ b/docs/KEV_CROSSREF.md @@ -4,7 +4,7 @@ Which SKELETONKEY modules cover CVEs that CISA has observed exploited in the wild per the Known Exploited Vulnerabilities catalog. Refreshed via `tools/refresh-cve-metadata.py`. -**12 of 34 modules cover KEV-listed CVEs.** +**13 of 36 modules cover KEV-listed CVEs.** ## In KEV (prioritize patching) @@ -22,6 +22,7 @@ Refreshed via `tools/refresh-cve-metadata.py`. | CVE-2025-32463 | 2025-09-29 | CWE-829 | `sudo_chwoot_cve_2025_32463` | | CVE-2021-22555 | 2025-10-06 | CWE-787 | `netfilter_xtcompat_cve_2021_22555` | | CVE-2018-14634 | 2026-01-26 | CWE-190 | `mutagen_astronomy_cve_2018_14634` | +| CVE-2022-0492 | 2026-06-02 | CWE-287 | `cgroup_release_agent_cve_2022_0492` | ## Not in KEV @@ -36,7 +37,6 @@ and are technically reachable. "Not in KEV" is not the same as | CVE-2020-14386 | CWE-250 | `af_packet2_cve_2020_14386` | | CVE-2020-29661 | CWE-416 | `tioscpgrp_cve_2020_29661` | | CVE-2021-33909 | CWE-190 | `sequoia_cve_2021_33909` | -| CVE-2022-0492 | CWE-287 | `cgroup_release_agent_cve_2022_0492` | | CVE-2022-25636 | CWE-269 | `nft_fwd_dup_cve_2022_25636` | | CVE-2022-2588 | CWE-416 | `cls_route4_cve_2022_2588` | | CVE-2023-0179 | CWE-190 | `nft_payload_cve_2023_0179` | @@ -48,8 +48,10 @@ and are technically reachable. "Not in KEV" is not the same as | CVE-2023-4622 | CWE-416 | `af_unix_gc_cve_2023_4622` | | CVE-2024-26581 | ? | `nft_pipapo_cve_2024_26581` | | CVE-2024-50264 | CWE-416 | `vsock_uaf_cve_2024_50264` | +| CVE-2025-32462 | CWE-863 | `sudo_host_cve_2025_32462` | | CVE-2025-6019 | CWE-250 | `udisks_libblockdev_cve_2025_6019` | | CVE-2026-31635 | CWE-130 | `dirtydecrypt_cve_2026_31635` | | CVE-2026-41651 | CWE-367 | `pack2theroot_cve_2026_41651` | | CVE-2026-43494 | ? | `pintheft_cve_2026_43494` | | CVE-2026-46300 | CWE-787 | `fragnesia_cve_2026_46300` | +| CVE-2026-46333 | CWE-269 | `ptrace_pidfd_cve_2026_46333` | diff --git a/docs/LAUNCH.md b/docs/LAUNCH.md index a7818e7..783c466 100644 --- a/docs/LAUNCH.md +++ b/docs/LAUNCH.md @@ -26,6 +26,7 @@ haven't been maintained in years. ```bash curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \ + && export PATH="$HOME/.local/bin:$PATH" \ && skeletonkey --auto --i-know ``` diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index d7d0053..81a0dfa 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -1,3 +1,37 @@ +## SKELETONKEY v0.9.9 β€” install.sh needs no root; CVE-2022-0492 KEV drift + +Two maintenance fixes, no new modules. + +**`install.sh` never escalates to sudo.** SKELETONKEY is a privilege- +escalation tool β€” the operator by definition does *not* have root yet, so +the installer must not demand it. The old default wrote to `/usr/local/bin` +and fell back to `sudo mv` when that wasn't writable, prompting for a +password on exactly the unprivileged accounts this tool targets. It now +installs sudo-free: `/usr/local/bin` is used only when already writable, +otherwise it falls back to a per-user `$HOME/.local/bin` (honoring +`XDG_BIN_HOME`), created as needed. An explicit `SKELETONKEY_PREFIX` is +honored exactly and errors rather than escalating if unwritable. When the +chosen dir isn't on `$PATH` the installer prints the absolute path, and the +documented `curl … | sh && skeletonkey --auto --i-know` one-liner now +prepends `$HOME/.local/bin` to `$PATH` so it resolves on a fresh login. The +quickstart no longer prefixes `sudo` to `--scan`/`--audit`/`--auto` β€” +detection and escalation run as the unprivileged user; only writing audit +rules into `/etc/audit` legitimately needs root. + +**Federal metadata drift (the failing scheduled build).** The weekly +`drift-check` caught two upstream changes since v0.9.8: + +- **CVE-2022-0492 entered CISA KEV (2026-06-02).** The cgroup v1 + `release_agent` container-escape (`cgroup_release_agent`) is now on the + Known Exploited Vulnerabilities catalog. The corpus reports **13 of 36** + modules covering KEV-listed CVEs (was 12). +- **CVE-2026-46333 gained a CWE.** When `ptrace_pidfd` was added two weeks + after disclosure, NVD had not yet classified it; it is now **CWE-269** + (Improper Privilege Management). + +Refreshed `CVE_METADATA.json`, the generated `cve_metadata.c` table, and +`KEV_CROSSREF.md` accordingly (README + website counts updated). + ## SKELETONKEY v0.9.8 β€” two new LPE modules (ptrace_pidfd, sudo_host) Adds the two most compelling recent Linux LPEs not already in the corpus, diff --git a/docs/app.js b/docs/app.js index b13e161..c64e784 100644 --- a/docs/app.js +++ b/docs/app.js @@ -10,7 +10,7 @@ * 1. typed install command in the hero * ============================================================ */ const installCmd = - 'curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \\\n && skeletonkey --auto --i-know'; + 'curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \\\n && export PATH="$HOME/.local/bin:$PATH" \\\n && skeletonkey --auto --i-know'; const typedEl = document.getElementById('install-typed'); const cursorEl = document.getElementById('install-cursor'); diff --git a/docs/index.html b/docs/index.html index cfbb936..e4b973d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,7 +4,7 @@ SKELETONKEY β€” Linux LPE corpus, VM-verified, SOC-ready detection - + @@ -56,7 +56,7 @@
- v0.9.8 β€” released 2026-06-02 + v0.9.9 β€” released 2026-06-08

SKELETONKEY @@ -227,7 +227,7 @@ uid=0(root) gid=0(root)
β˜…

CISA KEV prioritized

- 12 of 36 CVEs in the corpus are in CISA's Known Exploited + 13 of 36 CVEs in the corpus are in CISA's Known Exploited Vulnerabilities catalog β€” actively exploited in the wild. Refreshed on demand via tools/refresh-cve-metadata.py.

@@ -600,7 +600,7 @@ uid=0(root) gid=0(root) who found the bugs.

diff --git a/install.sh b/install.sh index a1c725d..a503981 100755 --- a/install.sh +++ b/install.sh @@ -28,7 +28,11 @@ set -eu REPO="${SKELETONKEY_REPO:-KaraZajac/SKELETONKEY}" VERSION="${SKELETONKEY_VERSION:-latest}" -PREFIX="${SKELETONKEY_PREFIX:-/usr/local/bin}" +# PREFIX resolution is deferred until install time so we can pick a +# sudo-free default. SKELETONKEY is a privilege-escalation tool β€” by +# definition the operator does NOT have root yet, so the installer must +# NEVER need sudo. Empty here means "auto-pick a writable dir below". +PREFIX="${SKELETONKEY_PREFIX:-}" log() { printf '[\033[1;36m*\033[0m] %s\n' "$*" >&2; } ok() { printf '[\033[1;32m+\033[0m] %s\n' "$*" >&2; } @@ -108,29 +112,49 @@ fi chmod +x "$tmp/skeletonkey" -# Install. Try $PREFIX directly; if not writable, sudo. -target_path="$PREFIX/skeletonkey" -if [ -w "$PREFIX" ] || [ "$(id -u)" -eq 0 ]; then - mv "$tmp/skeletonkey" "$target_path" -elif command -v sudo >/dev/null 2>&1; then - log "$PREFIX needs sudo; you may be prompted for password" - sudo mv "$tmp/skeletonkey" "$target_path" +# Choose install dir β€” NEVER escalate to sudo. If the user pinned +# SKELETONKEY_PREFIX we honor it exactly (creating it if needed) and +# error rather than escalate when it isn't writable. Otherwise prefer +# /usr/local/bin only when it happens to already be writable, and fall +# back to a guaranteed per-user dir ($HOME/.local/bin) that needs no +# privileges. This keeps `curl ... | sh` password-free for the exact +# users this tool is meant for: unprivileged accounts. +if [ -n "$PREFIX" ]; then + [ -d "$PREFIX" ] || mkdir -p "$PREFIX" 2>/dev/null \ + || fail "cannot create SKELETONKEY_PREFIX=$PREFIX" + [ -w "$PREFIX" ] || fail "SKELETONKEY_PREFIX=$PREFIX not writable (the installer never uses sudo β€” pick a writable dir)" +elif [ -w /usr/local/bin ]; then + PREFIX=/usr/local/bin else - fail "$PREFIX not writable and sudo not available. Try SKELETONKEY_PREFIX=\$HOME/.local/bin" + PREFIX="${XDG_BIN_HOME:-$HOME/.local/bin}" + mkdir -p "$PREFIX" 2>/dev/null || fail "cannot create $PREFIX" fi +target_path="$PREFIX/skeletonkey" +mv "$tmp/skeletonkey" "$target_path" || fail "failed to install to $target_path" ok "installed: $target_path" + +# ~/.local/bin is frequently absent from PATH on fresh accounts β€” tell +# the user how to invoke it rather than letting `skeletonkey` 404. +case ":$PATH:" in + *":$PREFIX:"*) : ;; + *) log "note: $PREFIX is not on \$PATH β€” run it as $target_path, or add the dir to PATH" ;; +esac + "$target_path" --version cat >&2 < #include -#define SKELETONKEY_VERSION "0.9.8" +#define SKELETONKEY_VERSION "0.9.9" static const char BANNER[] = "\n" diff --git a/tools/__pycache__/refresh-cve-metadata.cpython-314.pyc b/tools/__pycache__/refresh-cve-metadata.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e725055e115fd730061eafebf6edbd7b3454685e GIT binary patch literal 20184 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