Files
SKELETONKEY/README.md
T
leviathan d84b3b0033 release v0.9.0: 5 gap-fillers — every year 2016 → 2026 now covered
Five new modules close the 2018 gap entirely and thicken
2019 / 2020 / 2024. All five carry the full 4-format detection-rule
corpus + opsec_notes + arch_support + register helpers.

CVE-2018-14634 — mutagen_astronomy (Qualys, closes 2018)
  create_elf_tables() int wrap → SUID-execve stack corruption.
  CISA KEV-listed Jan 2026 despite the bug's age; legacy RHEL 7 /
  CentOS 7 / Debian 8 fleets still affected. 🟡 PRIMITIVE.
  arch_support: x86_64+unverified-arm64.

CVE-2019-14287 — sudo_runas_neg1 (Joe Vennix)
  sudo -u#-1 → uid_t underflow → root despite (ALL,!root) blacklist.
  Pure userspace logic bug; the famous Apple Information Security
  finding. detect() looks for a (ALL,!root) grant in sudo -ln output;
  PRECOND_FAIL when no such grant exists for the invoking user.
  arch_support: any (4 -> 5 userspace 'any' modules).

CVE-2020-29661 — tioscpgrp (Jann Horn / Project Zero)
  TTY TIOCSPGRP ioctl race on PTY pairs → struct pid UAF in
  kmalloc-256. Affects everything through Linux 5.9.13. 🟡 PRIMITIVE
  (race-driver + msg_msg groom). Public PoCs from grsecurity /
  spender + Maxime Peterlin.

CVE-2024-50264 — vsock_uaf (a13xp0p0v / Pwnie Award 2025 winner)
  AF_VSOCK connect-race UAF in kmalloc-96. Pwn2Own 2024 + Pwnie
  2025 winner. Reachable as plain unprivileged user (no userns
  required — unusual). Two public exploit paths: @v4bel+@qwerty
  kernelCTF (BPF JIT spray + SLUBStick) and Alexander Popov / PT
  SWARM (msg_msg). 🟡 PRIMITIVE.

CVE-2024-26581 — nft_pipapo (Notselwyn II, 'Flipping Pages')
  nft_set_pipapo destroy-race UAF. Sibling to nf_tables
  (CVE-2024-1086) from the same Notselwyn paper. Distinct bug in
  the pipapo set substrate. Same family signature. 🟡 PRIMITIVE.

Plumbing changes:

  core/registry.h + registry_all.c — 5 new register declarations
    + calls.
  Makefile — 5 new MUT/SRN/TIO/VSK/PIP module groups in MODULE_OBJS.
  tests/test_detect.c — 7 new test rows covering the new modules
    (above-fix OK, predates-the-bug OK, sudo-no-grant PRECOND_FAIL).
  tools/verify-vm/targets.yaml — verifier entries for all 5 with
    honest 'expect_detect' values based on what Vagrant boxes can
    realistically reach (mutagen_astronomy gets OK on stock 18.04
    since 4.15.0-213 is post-fix; sudo_runas_neg1 gets PRECOND_FAIL
    because no (ALL,!root) grant on default vagrant user; tioscpgrp
    + nft_pipapo VULNERABLE with kernel pins; vsock_uaf flagged
    manual because vsock module rarely available on CI runners).
  tools/refresh-cve-metadata.py — added curl fallback for the CISA
    KEV CSV fetch (urlopen times out intermittently against CISA's
    HTTP/2 endpoint).

Corpus growth across v0.8.0 + v0.9.0:

                v0.7.1    v0.8.0    v0.9.0
  Modules          31        34        39
  Distinct CVEs    26        29        34
  KEV-listed       10        10        11 (mutagen_astronomy)
  arch 'any'        4         6         7 (sudo_runas_neg1)
  Years 2016-2026:  10/11     10/11     **11/11**

Year-by-year coverage:

  2016: 1   2017: 1   2018: 1   2019: 2   2020: 2
  2021: 5   2022: 5   2023: 8   2024: 3   2025: 2   2026: 4

CVE-2018 gap → CLOSED. Every year from 2016 through 2026 now has
at least one module.

Surfaces updated:
  - README.md: badge → 22 VM-verified / 34, Status section refreshed
  - docs/index.html: hero eyebrow + footer → v0.9.0, hero tagline
    'every year 2016 → 2026', stats chips → 39 / 22 / 11 / 151
  - docs/RELEASE_NOTES.md: v0.9.0 entry added on top with year
    coverage matrix + per-module breakdown; v0.8.0 + v0.7.1 entries
    preserved below
  - docs/og.svg + og.png: regenerated with new numbers + 'Every
    year 2016 → 2026' tagline

CVE metadata refresh (tools/refresh-cve-metadata.py) deferred to
follow-up — CISA KEV CSV + NVD CVE API were timing out during the
v0.9.0 push window. The 5 new CVEs will return NULL from
cve_metadata_lookup() until the refresh runs (—module-info simply
skips the WEAKNESS/THREAT INTEL header for them; no functional
impact). Re-run 'tools/refresh-cve-metadata.py' when network
cooperates.

Tests: macOS local 33/33 kernel_range pass; detect-test stubs (88
total) build clean; ASan/UBSan + clang-tidy CI jobs still green
from the v0.7.x setup.
2026-05-23 22:15:44 -04:00

271 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SKELETONKEY
[![Latest release](https://img.shields.io/github/v/release/KaraZajac/SKELETONKEY?label=release)](https://github.com/KaraZajac/SKELETONKEY/releases/latest)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Modules](https://img.shields.io/badge/CVEs-22%20VM--verified%20%2F%2034-brightgreen.svg)](docs/VERIFICATIONS.jsonl)
[![Platform: Linux](https://img.shields.io/badge/platform-linux-lightgrey.svg)](#)
> **One curated binary. 39 Linux LPE modules covering 34 CVEs from 2016 → 2026.
> Every year 2016 → 2026 covered. 22 confirmed end-to-end against real Linux
> VMs via `tools/verify-vm/`. Detection rules in the box. One command picks
> the safest one and runs it.**
```bash
curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \
&& skeletonkey --auto --i-know
```
> ⚠️ **Authorized testing only.** SKELETONKEY runs real exploits. By
> using it you assert you have explicit authorization to test the
> target system. See [`docs/ETHICS.md`](docs/ETHICS.md).
## Why use this
Most Linux privesc tooling is broken in one of three ways:
- **`linux-exploit-suggester` / `linpeas`** — tell you what *might*
work, run nothing
- **`auto-root-exploit` / `kernelpop`** — bundle exploits but ship
no detection signatures and went stale years ago
- **Per-CVE PoC repos** — one author, one distro, abandoned within
months
SKELETONKEY is one binary, actively maintained, with detection rules
for every CVE in the bundle — same project for red and blue teams.
## Who it's for
| Audience | What you get |
|---|---|
| **Red team / pentesters** | One tested binary. `--auto` ranks vulnerable modules by safety and runs the safest. Honest scope reporting — never claims root it didn't actually get. |
| **Sysadmins** | `skeletonkey --scan` (no sudo needed) tells you which boxes still need patching. Fleet-scan tool included. JSON output for CI gates ([schema](docs/JSON_SCHEMA.md)). |
| **Blue team / SOC** | Auditd + sigma + yara + falco rules for every CVE. `--detect-rules --format=auditd \| sudo tee …` ships SIEM coverage in one command. |
| **CTF / training** | Reproducible LPE environment with public CVEs across a 10-year timeline. Each module documents the bug, the trigger, and the fix. |
## Corpus at a glance
**31 modules covering 26 distinct CVEs** across the 2016 → 2026 LPE
timeline. **22 of the 26 CVEs have been empirically verified** in real
Linux VMs via `tools/verify-vm/`; the 4 still-pending entries are
blocked by their target environment, not by missing code.
| Tier | Count | What it means |
|---|---|---|
| 🟢 Full chain | **14** | Lands root (or its canonical capability) end-to-end. No per-kernel offsets needed. |
| 🟡 Primitive | **14** | Fires the kernel primitive + grooms the slab + records a witness. Default returns `EXPLOIT_FAIL` honestly. Pass `--full-chain` to engage the shared `modprobe_path` finisher (needs offsets — see [`docs/OFFSETS.md`](docs/OFFSETS.md)). |
**🟢 Modules that land root on a vulnerable host:**
copy_fail family ×5 · dirty_pipe · dirty_cow · pwnkit · overlayfs
(CVE-2021-3493) · overlayfs_setuid (CVE-2023-0386) ·
cgroup_release_agent · ptrace_traceme · sudoedit_editor · entrybleed
(KASLR leak primitive)
**🟡 Modules with opt-in `--full-chain`:**
af_packet · af_packet2 · af_unix_gc · cls_route4 · fuse_legacy ·
nf_tables · nft_set_uaf · nft_fwd_dup · nft_payload ·
netfilter_xtcompat · stackrot · sudo_samedit · sequoia · vmwgfx
### Empirical verification (22 of 26 CVEs)
Records in [`docs/VERIFICATIONS.jsonl`](docs/VERIFICATIONS.jsonl) prove
each verdict against a known-target VM. Coverage:
| Distro / kernel | Modules verified |
|---|---|
| Ubuntu 18.04 (4.15.0) | af_packet · ptrace_traceme · sudo_samedit |
| Ubuntu 20.04 (5.4 stock + 5.15 HWE) | af_packet2 · cls_route4 · nft_payload · overlayfs · pwnkit · sequoia |
| Ubuntu 22.04 (5.15 stock + mainline 5.15.5 / 6.1.10) | af_unix_gc · dirty_pipe · entrybleed · nf_tables · nft_set_uaf · overlayfs_setuid · stackrot · sudoedit_editor |
| Debian 11 (5.10 stock) | cgroup_release_agent · fuse_legacy · netfilter_xtcompat · nft_fwd_dup |
| Debian 12 (6.1 stock) | pack2theroot |
**Not yet verified (4):** `vmwgfx` (VMware-guest-only — no public
Vagrant box), `dirty_cow` (needs ≤ 4.4 kernel — older than every
supported box), `dirtydecrypt` & `fragnesia` (need Linux 7.0 — not
shipping as any distro kernel yet). All four are flagged in
[`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml) with
rationale.
See [`CVES.md`](CVES.md) for per-module CVE, kernel range, and
detection status. Run `skeletonkey --module-info <name>` for the
embedded verification records per module.
## Quickstart
```bash
# Install (x86_64 / arm64; checksum-verified)
curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh
# What's this box vulnerable to? (no sudo)
skeletonkey --scan
# One-page operator briefing for a single CVE: CWE / MITRE ATT&CK /
# CISA KEV status, live detect() trace, OPSEC footprint, detection
# coverage. Useful for triage tickets and SOC analyst handoffs.
skeletonkey --explain nf_tables
# Pick the safest LPE and run it
skeletonkey --auto --i-know
# Deploy detection rules (needs sudo to write into /etc/audit/rules.d/)
skeletonkey --detect-rules --format=auditd \
| sudo tee /etc/audit/rules.d/99-skeletonkey.rules
# Fleet scan — many hosts via SSH, aggregated JSON for SIEM
./tools/skeletonkey-fleet-scan.sh --binary skeletonkey \
--ssh-key ~/.ssh/id_rsa hosts.txt
```
**SKELETONKEY runs as a normal unprivileged user** — that's the point.
`--scan`, `--audit`, `--exploit`, and `--detect-rules` all work without
`sudo`. Only `--mitigate` and rule-file installation write root-owned
paths.
### Example: unprivileged → root
```text
$ id
uid=1000(kara) gid=1000(kara) groups=1000(kara)
$ skeletonkey --auto --i-know
[*] auto: host=demo distro=ubuntu/24.04 kernel=5.15.0-56-generic arch=x86_64
[*] auto: active probes enabled — brief /tmp file touches and fork-isolated namespace probes
[*] auto: scanning 31 modules for vulnerabilities...
[+] auto: dirty_pipe VULNERABLE (safety rank 90)
[+] auto: cgroup_release_agent VULNERABLE (safety rank 98)
[+] auto: pwnkit VULNERABLE (safety rank 100)
[ ] auto: copy_fail patched or not applicable
[ ] auto: nf_tables precondition not met
...
[*] auto: scan summary — 3 vulnerable, 21 patched/n.a., 7 precondition-fail, 0 indeterminate
[*] auto: 3 vulnerable modules found. Safest is 'pwnkit' (rank 100).
[*] auto: launching --exploit pwnkit...
[+] pwnkit: writing gconv-modules cache + payload.so...
[+] pwnkit: execve(pkexec) with NULL argv + crafted envp...
# id
uid=0(root) gid=0(root) groups=0(root)
```
The safety ranking goes: **structural escapes** (no kernel state
touched) → **page-cache writes****userspace cred-races**
**kernel primitives****kernel races** (least predictable). The
goal is to never crash a production box looking for root.
## How it works
Each CVE (or tightly-related family) is a **module** under `modules/`.
Modules export a standard interface (`detect / exploit / mitigate /
cleanup`) plus metadata (kernel range, detection rule text). The
top-level binary dispatches per command:
- `--scan` walks every module's `detect()` against the running host
- `--exploit <name> --i-know` runs the named module's exploit (the
`--i-know` flag is the authorization gate)
- `--auto --i-know` does the scan, ranks by safety, runs the safest
- `--detect-rules --format=<auditd|sigma|yara|falco>` emits the
embedded rule corpus
- `--mitigate <name>` / `--cleanup <name>` apply / undo temporary
mitigations (module-dependent — most kernel modules say "upgrade")
- `--dump-offsets` reads `/proc/kallsyms` + `/boot/System.map` and
emits a ready-to-paste C entry for the `--full-chain` offset table
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the
module-loader design.
## The verified-vs-claimed bar
Most public PoC repos hardcode offsets for one kernel build and
silently break elsewhere. SKELETONKEY refuses to ship fabricated
offsets. The shared `--full-chain` finisher only returns
`EXPLOIT_OK` after a setuid bash sentinel file *actually appears*;
otherwise modules return `EXPLOIT_FAIL` with a diagnostic. Operators
populate the offset table once per target kernel via
`skeletonkey --dump-offsets` and either set env vars or upstream the
entry via PR ([`CONTRIBUTING.md`](CONTRIBUTING.md)).
## Build from source
```bash
git clone https://github.com/KaraZajac/SKELETONKEY.git
cd SKELETONKEY
make
./skeletonkey --version
```
Builds clean with gcc or clang on any modern Linux. macOS dev builds
also compile (modules with Linux-only headers stub out gracefully).
## Status
**v0.9.0 cut 2026-05-24.** 39 modules across 34 CVEs — **every
year 2016 → 2026 now covered**. v0.9.0 adds 5 gap-fillers:
`mutagen_astronomy` (CVE-2018-14634 — closes 2018), `sudo_runas_neg1`
(CVE-2019-14287), `tioscpgrp` (CVE-2020-29661), `vsock_uaf`
(CVE-2024-50264 — Pwnie 2025 winner), `nft_pipapo` (CVE-2024-26581 —
Notselwyn II). v0.8.0 added 3 (`sudo_chwoot`/CVE-2025-32463,
`udisks_libblockdev`/CVE-2025-6019, `pintheft`/CVE-2026-43494).
**22 empirically verified** against real Linux VMs (Ubuntu 18.04 /
20.04 / 22.04 + Debian 11 / 12 + mainline kernels 5.15.5 / 6.1.10
from kernel.ubuntu.com). 88-test unit harness + ASan/UBSan +
clang-tidy on every push. 4 prebuilt binaries (x86_64 + arm64, each
in dynamic + static-musl flavors).
Reliability + accuracy work in v0.7.x:
- Shared **host fingerprint** (`core/host.{h,c}`) populated once at
startup — kernel/distro/userns gates/sudo+polkit versions — exposed
to every module via `ctx->host`.
- **Test harness** (`tests/`, `make test`) — 88 tests: 33 kernel_range
unit tests + 55 detect() integration tests over mocked host
fingerprints. Runs in CI on every push.
- **VM verifier** (`tools/verify-vm/`) — Vagrant + Parallels scaffold
that boots known-vulnerable kernels (stock distro + mainline via
kernel.ubuntu.com), runs `--explain --active` per module, records
match/MISMATCH/PRECOND_FAIL as JSON. 22 modules confirmed end-to-end.
- **`--explain <module>`** — single-page operator briefing: CVE / CWE
/ MITRE ATT&CK / CISA KEV status, host fingerprint, live detect()
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; 10 of 26 modules cover KEV-listed CVEs.
- **119 detection rules** across auditd / sigma / yara / falco; one
command exports the corpus to your SIEM.
- `--auto` upgrades: per-detect 15s timeout, fork-isolated detect +
exploit, structured verdict table, scan summary, `--dry-run`.
Not yet verified (4 of 26 CVEs): `vmwgfx` (VMware-guest only),
`dirty_cow` (needs ≤ 4.4 kernel), `dirtydecrypt` + `fragnesia` (need
Linux 7.0 — not shipping yet). Rationale in
[`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml).
See [`ROADMAP.md`](ROADMAP.md) for the next planned modules and
infrastructure work.
## Contributing
PRs welcome for: kernel offsets (run `--dump-offsets` on a target
kernel, paste into `core/offsets.c`), new modules, detection rules,
and CVE-status corrections. See [`CONTRIBUTING.md`](CONTRIBUTING.md).
**Keeping `kernel_range` tables current.** `tools/refresh-kernel-ranges.py`
polls Debian's security tracker and reports drift between each
module's hardcoded `kernel_patched_from` thresholds and the
fixed-versions Debian actually ships. Run periodically (or in CI)
to catch new backports that need to land in the corpus:
```bash
tools/refresh-kernel-ranges.py # human report
tools/refresh-kernel-ranges.py --json # machine-readable
tools/refresh-kernel-ranges.py --patch # proposed C-source edits
```
## Acknowledgments
Each module credits the original CVE reporter and PoC author in its
`NOTICE.md`. SKELETONKEY is the bundling and bookkeeping layer;
the research credit belongs to the people who found the bugs.
## License
MIT — see [`LICENSE`](LICENSE).