release v0.9.1: VM verification sweep 22 → 27
release / build (arm64) (push) Waiting to run
release / build (x86_64) (push) Waiting to run
release / build (x86_64-static / musl) (push) Waiting to run
release / build (arm64-static / musl) (push) Waiting to run
release / release (push) Blocked by required conditions

Five more CVEs empirically confirmed end-to-end against real Linux VMs:
- CVE-2019-14287 sudo_runas_neg1 (Ubuntu 18.04 + sudoers grant)
- CVE-2020-29661 tioscpgrp        (Ubuntu 20.04 pinned to 5.4.0-26)
- CVE-2024-26581 nft_pipapo       (Ubuntu 22.04 + mainline 5.15.5)
- CVE-2025-32463 sudo_chwoot      (Ubuntu 22.04 + sudo 1.9.16p1 from source)
- CVE-2025-6019  udisks_libblockdev (Debian 12 + udisks2 + polkit rule)

Required real plumbing work:
- Per-module provisioner hook (tools/verify-vm/provisioners/<module>.sh)
- Two-phase provision in verify.sh (prep → reboot if needed → verify)
  fixes silent-fail where new kernel installed but VM never rebooted
- GRUB_DEFAULT pinning in both pin-kernel and pin-mainline blocks
  (kernel downgrades like 5.4.0-169 → 5.4.0-26 now actually boot the target)
- Old-mainline URL fallback in pin-mainline (≤ 4.15 debs at /v$KVER/ not /amd64/)

mutagen_astronomy marked manual: true — mainline 4.14.70 kernel-panics on
Ubuntu 18.04's rootfs ('Failed to execute /init (error -8)' — kernel config
mismatch). Genuinely needs a CentOS 6 / Debian 7 image.
This commit is contained in:
2026-05-23 23:35:02 -04:00
parent 270ddc1681
commit 8ac041a295
12 changed files with 230 additions and 59 deletions
+22 -19
View File
@@ -2,11 +2,11 @@
[![Latest release](https://img.shields.io/github/v/release/KaraZajac/SKELETONKEY?label=release)](https://github.com/KaraZajac/SKELETONKEY/releases/latest) [![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) [![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) [![Modules](https://img.shields.io/badge/CVEs-27%20VM--verified%20%2F%2034-brightgreen.svg)](docs/VERIFICATIONS.jsonl)
[![Platform: Linux](https://img.shields.io/badge/platform-linux-lightgrey.svg)](#) [![Platform: Linux](https://img.shields.io/badge/platform-linux-lightgrey.svg)](#)
> **One curated binary. 39 Linux LPE modules covering 34 CVEs from 2016 → 2026. > **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 > Every year 2016 → 2026 covered. 27 confirmed end-to-end against real Linux
> VMs via `tools/verify-vm/`. Detection rules in the box. One command picks > VMs via `tools/verify-vm/`. Detection rules in the box. One command picks
> the safest one and runs it.** > the safest one and runs it.**
@@ -44,10 +44,11 @@ for every CVE in the bundle — same project for red and blue teams.
## Corpus at a glance ## Corpus at a glance
**31 modules covering 26 distinct CVEs** across the 2016 → 2026 LPE **39 modules covering 34 distinct CVEs** across the 2016 → 2026 LPE
timeline. **22 of the 26 CVEs have been empirically verified** in real timeline. **27 of the 34 CVEs have been empirically verified** in real
Linux VMs via `tools/verify-vm/`; the 4 still-pending entries are Linux VMs via `tools/verify-vm/`; the 7 still-pending entries are
blocked by their target environment, not by missing code. blocked by their target environment (legacy hypervisor, EOL kernel, or
not-yet-shipped Linux 7.0), not by missing code.
| Tier | Count | What it means | | Tier | Count | What it means |
|---|---|---| |---|---|---|
@@ -65,25 +66,27 @@ af_packet · af_packet2 · af_unix_gc · cls_route4 · fuse_legacy ·
nf_tables · nft_set_uaf · nft_fwd_dup · nft_payload · nf_tables · nft_set_uaf · nft_fwd_dup · nft_payload ·
netfilter_xtcompat · stackrot · sudo_samedit · sequoia · vmwgfx netfilter_xtcompat · stackrot · sudo_samedit · sequoia · vmwgfx
### Empirical verification (22 of 26 CVEs) ### Empirical verification (27 of 34 CVEs)
Records in [`docs/VERIFICATIONS.jsonl`](docs/VERIFICATIONS.jsonl) prove Records in [`docs/VERIFICATIONS.jsonl`](docs/VERIFICATIONS.jsonl) prove
each verdict against a known-target VM. Coverage: each verdict against a known-target VM. Coverage:
| Distro / kernel | Modules verified | | Distro / kernel | Modules verified |
|---|---| |---|---|
| Ubuntu 18.04 (4.15.0) | af_packet · ptrace_traceme · sudo_samedit | | Ubuntu 18.04 (4.15.0, sudo 1.8.21p2) | af_packet · ptrace_traceme · sudo_samedit · sudo_runas_neg1 |
| Ubuntu 20.04 (5.4 stock + 5.15 HWE) | af_packet2 · cls_route4 · nft_payload · overlayfs · pwnkit · sequoia | | Ubuntu 20.04 (5.4.0-26 pinned + 5.15 HWE) | af_packet2 · cls_route4 · nft_payload · overlayfs · pwnkit · sequoia · tioscpgrp |
| 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 | | Ubuntu 22.04 (5.15 stock + mainline 5.15.5 / 6.1.10) | af_unix_gc · dirty_pipe · entrybleed · nf_tables · nft_set_uaf · nft_pipapo · overlayfs_setuid · stackrot · sudoedit_editor · sudo_chwoot |
| Debian 11 (5.10 stock) | cgroup_release_agent · fuse_legacy · netfilter_xtcompat · nft_fwd_dup | | Debian 11 (5.10 stock) | cgroup_release_agent · fuse_legacy · netfilter_xtcompat · nft_fwd_dup |
| Debian 12 (6.1 stock) | pack2theroot | | Debian 12 (6.1 stock + udisks2 / polkit allow rule) | pack2theroot · udisks_libblockdev |
**Not yet verified (4):** `vmwgfx` (VMware-guest-only — no public **Not yet verified (7):** `vmwgfx` (VMware-guest-only — no public Vagrant
Vagrant box), `dirty_cow` (needs ≤ 4.4 kernel — older than every box), `dirty_cow` (needs ≤ 4.4 kernel — older than every supported box),
supported box), `dirtydecrypt` & `fragnesia` (need Linux 7.0 — not `mutagen_astronomy` (mainline 4.14.70 kernel-panics on Ubuntu 18.04
shipping as any distro kernel yet). All four are flagged in rootfs — needs CentOS 6 / Debian 7), `pintheft` & `vsock_uaf` (kernel
[`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml) with modules not loaded on common Vagrant boxes), `dirtydecrypt` & `fragnesia`
rationale. (need Linux 7.0 — not shipping as any distro kernel yet). All seven 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 See [`CVES.md`](CVES.md) for per-module CVE, kernel range, and
detection status. Run `skeletonkey --module-info <name>` for the detection status. Run `skeletonkey --module-info <name>` for the
@@ -205,7 +208,7 @@ year 2016 → 2026 now covered**. v0.9.0 adds 5 gap-fillers:
(CVE-2024-50264 — Pwnie 2025 winner), `nft_pipapo` (CVE-2024-26581 — (CVE-2024-50264 — Pwnie 2025 winner), `nft_pipapo` (CVE-2024-26581 —
Notselwyn II). v0.8.0 added 3 (`sudo_chwoot`/CVE-2025-32463, Notselwyn II). v0.8.0 added 3 (`sudo_chwoot`/CVE-2025-32463,
`udisks_libblockdev`/CVE-2025-6019, `pintheft`/CVE-2026-43494). `udisks_libblockdev`/CVE-2025-6019, `pintheft`/CVE-2026-43494).
**22 empirically verified** against real Linux VMs (Ubuntu 18.04 / **27 empirically verified** against real Linux VMs (Ubuntu 18.04 /
20.04 / 22.04 + Debian 11 / 12 + mainline kernels 5.15.5 / 6.1.10 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 + from kernel.ubuntu.com). 88-test unit harness + ASan/UBSan +
clang-tidy on every push. 4 prebuilt binaries (x86_64 + arm64, each clang-tidy on every push. 4 prebuilt binaries (x86_64 + arm64, each
@@ -221,7 +224,7 @@ Reliability + accuracy work in v0.7.x:
- **VM verifier** (`tools/verify-vm/`) — Vagrant + Parallels scaffold - **VM verifier** (`tools/verify-vm/`) — Vagrant + Parallels scaffold
that boots known-vulnerable kernels (stock distro + mainline via that boots known-vulnerable kernels (stock distro + mainline via
kernel.ubuntu.com), runs `--explain --active` per module, records kernel.ubuntu.com), runs `--explain --active` per module, records
match/MISMATCH/PRECOND_FAIL as JSON. 22 modules confirmed end-to-end. match/MISMATCH/PRECOND_FAIL as JSON. 27 modules confirmed end-to-end.
- **`--explain <module>`** — single-page operator briefing: CVE / CWE - **`--explain <module>`** — single-page operator briefing: CVE / CWE
/ MITRE ATT&CK / CISA KEV status, host fingerprint, live detect() / MITRE ATT&CK / CISA KEV status, host fingerprint, live detect()
trace, OPSEC footprint, detection-rule coverage, verified-on trace, OPSEC footprint, detection-rule coverage, verified-on
+50
View File
@@ -136,6 +136,16 @@ const struct verification_record verifications[] = {
.actual_detect = "VULNERABLE", .actual_detect = "VULNERABLE",
.status = "match", .status = "match",
}, },
{
.module = "nft_pipapo",
.verified_at = "2026-05-24",
.host_kernel = "5.15.5-051505-generic",
.host_distro = "Ubuntu 22.04.3 LTS",
.vm_box = "generic/ubuntu2204",
.expect_detect = "VULNERABLE",
.actual_detect = "VULNERABLE",
.status = "match",
},
{ {
.module = "nft_set_uaf", .module = "nft_set_uaf",
.verified_at = "2026-05-23", .verified_at = "2026-05-23",
@@ -216,6 +226,26 @@ const struct verification_record verifications[] = {
.actual_detect = "VULNERABLE", .actual_detect = "VULNERABLE",
.status = "match", .status = "match",
}, },
{
.module = "sudo_chwoot",
.verified_at = "2026-05-24",
.host_kernel = "5.15.0-91-generic",
.host_distro = "Ubuntu 22.04.3 LTS",
.vm_box = "generic/ubuntu2204",
.expect_detect = "VULNERABLE",
.actual_detect = "VULNERABLE",
.status = "match",
},
{
.module = "sudo_runas_neg1",
.verified_at = "2026-05-24",
.host_kernel = "4.15.0-213-generic",
.host_distro = "Ubuntu 18.04.6 LTS",
.vm_box = "generic/ubuntu1804",
.expect_detect = "VULNERABLE",
.actual_detect = "VULNERABLE",
.status = "match",
},
{ {
.module = "sudo_samedit", .module = "sudo_samedit",
.verified_at = "2026-05-23", .verified_at = "2026-05-23",
@@ -236,6 +266,26 @@ const struct verification_record verifications[] = {
.actual_detect = "PRECOND_FAIL", .actual_detect = "PRECOND_FAIL",
.status = "match", .status = "match",
}, },
{
.module = "tioscpgrp",
.verified_at = "2026-05-24",
.host_kernel = "5.4.0-26-generic",
.host_distro = "Ubuntu 20.04.6 LTS",
.vm_box = "generic/ubuntu2004",
.expect_detect = "VULNERABLE",
.actual_detect = "VULNERABLE",
.status = "match",
},
{
.module = "udisks_libblockdev",
.verified_at = "2026-05-24",
.host_kernel = "6.1.0-17-amd64",
.host_distro = "Debian GNU/Linux 12 (bookworm)",
.vm_box = "generic/debian12",
.expect_detect = "VULNERABLE",
.actual_detect = "VULNERABLE",
.status = "match",
},
}; };
const size_t verifications_count = const size_t verifications_count =
+51
View File
@@ -1,3 +1,54 @@
## SKELETONKEY v0.9.1 — VM verification sweep (22 → 27)
Five more CVEs empirically confirmed end-to-end against real Linux VMs
via `tools/verify-vm/`:
| CVE | Module | Target environment |
|---|---|---|
| CVE-2019-14287 | `sudo_runas_neg1` | Ubuntu 18.04 (sudo 1.8.21p2 + `(ALL,!root)` grant via provisioner) |
| CVE-2020-29661 | `tioscpgrp` | Ubuntu 20.04 pinned to `5.4.0-26` (genuinely below the 5.4.85 backport) |
| CVE-2024-26581 | `nft_pipapo` | Ubuntu 22.04 + mainline `5.15.5` (below the 5.15.149 fix) |
| CVE-2025-32463 | `sudo_chwoot` | Ubuntu 22.04 + sudo `1.9.16p1` built from upstream into `/usr/local/bin` |
| CVE-2025-6019 | `udisks_libblockdev` | Debian 12 + `udisks2` 2.9.4 + polkit allow rule for the verifier user |
Footer goes from `22 empirically verified``27 empirically verified`.
### Verifier infrastructure (the why)
These verifications required real plumbing work that didn't exist before:
- **Per-module provisioner hook** (`tools/verify-vm/provisioners/<module>.sh`)
— per-target setup that doesn't belong in the Vagrantfile (build sudo
from source, install udisks2 + polkit rule, drop a sudoers grant) now
lives in checked-in scripts that re-run idempotently on every verify.
- **Two-phase provisioning** in `verify.sh` — prep provisioners run
first (install kernel, set grub default, drop polkit rule), then a
conditional reboot if `uname -r` doesn't match the target, then the
verifier proper. Fixes the silent-fail where the new kernel was
installed but the VM never actually rebooted into it.
- **GRUB_DEFAULT pin in both `pin-kernel` and `pin-mainline` blocks** —
without this, grub's debian-version-compare picks the highest-sorting
vmlinuz as default; for downgrades (stock 4.15 → mainline 4.14.70, or
stock 5.4.0-169 → pinned 5.4.0-26) the wrong kernel won boot.
- **Old-mainline URL fallback** — kernel.ubuntu.com puts ≤ 4.15 mainline
debs at `/v${KVER}/` not `/v${KVER}/amd64/`. Fallback handles both.
### Honest residuals — 7 of 34 still unverified
| Module | Why not verified |
|---|---|
| `vmwgfx` | needs a VMware guest; we're on Parallels |
| `dirty_cow` | needs ≤ 4.4 kernel — older than any supported Vagrant box |
| `mutagen_astronomy` | mainline 4.14.70 kernel-panics on Ubuntu 18.04 rootfs (`Failed to execute /init (error -8)` — kernel config mismatch). Genuinely needs CentOS 6 / Debian 7. |
| `pintheft` | needs RDS kernel module loaded (Arch only autoloads it) |
| `vsock_uaf` | needs `vsock_loopback` loaded — not autoloaded on common Vagrant boxes |
| `dirtydecrypt`, `fragnesia` | need Linux 7.0 — not yet shipping as any distro kernel |
All seven are flagged in `tools/verify-vm/targets.yaml` with `manual: true`
and a rationale.
---
## SKELETONKEY v0.9.0 — every year 2016 → 2026 now covered ## SKELETONKEY v0.9.0 — every year 2016 → 2026 now covered
Five gap-filling modules. Closes the 2018 hole entirely and thickens Five gap-filling modules. Closes the 2018 hole entirely and thickens
+5
View File
@@ -28,3 +28,8 @@
{"module":"af_unix_gc","verified_at":"2026-05-23T21:27:13Z","host_kernel":"5.15.5-051505-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"} {"module":"af_unix_gc","verified_at":"2026-05-23T21:27:13Z","host_kernel":"5.15.5-051505-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"nft_set_uaf","verified_at":"2026-05-23T21:30:41Z","host_kernel":"5.15.5-051505-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"} {"module":"nft_set_uaf","verified_at":"2026-05-23T21:30:41Z","host_kernel":"5.15.5-051505-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"stackrot","verified_at":"2026-05-23T21:34:12Z","host_kernel":"6.1.10-060110-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"} {"module":"stackrot","verified_at":"2026-05-23T21:34:12Z","host_kernel":"6.1.10-060110-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"sudo_chwoot","verified_at":"2026-05-24T02:39:11Z","host_kernel":"5.15.0-91-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"udisks_libblockdev","verified_at":"2026-05-24T02:44:17Z","host_kernel":"6.1.0-17-amd64","host_distro":"Debian GNU/Linux 12 (bookworm)","vm_box":"generic/debian12","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"nft_pipapo","verified_at":"2026-05-24T03:27:10Z","host_kernel":"5.15.5-051505-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"sudo_runas_neg1","verified_at":"2026-05-24T03:29:18Z","host_kernel":"4.15.0-213-generic","host_distro":"Ubuntu 18.04.6 LTS","vm_box":"generic/ubuntu1804","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
{"module":"tioscpgrp","verified_at":"2026-05-24T03:31:08Z","host_kernel":"5.4.0-26-generic","host_distro":"Ubuntu 20.04.6 LTS","vm_box":"generic/ubuntu2004","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
+6 -6
View File
@@ -4,9 +4,9 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SKELETONKEY — Linux LPE corpus, VM-verified, SOC-ready detection</title> <title>SKELETONKEY — Linux LPE corpus, VM-verified, SOC-ready detection</title>
<meta name="description" content="One binary. 31 Linux privilege-escalation modules from 2016 to 2026. 22 of 26 CVEs empirically verified in real Linux VMs. 10 KEV-listed. 119 detection rules across auditd/sigma/yara/falco. MITRE ATT&CK and CWE annotated. --explain gives operator briefings."> <meta name="description" content="One binary. 39 Linux privilege-escalation modules from 2016 to 2026. 27 of 34 CVEs empirically verified in real Linux VMs. 10 KEV-listed. 151 detection rules across auditd/sigma/yara/falco. MITRE ATT&CK and CWE annotated. --explain gives operator briefings.">
<meta property="og:title" content="SKELETONKEY — Linux LPE corpus, VM-verified"> <meta property="og:title" content="SKELETONKEY — Linux LPE corpus, VM-verified">
<meta property="og:description" content="31 Linux LPE modules; 22 of 26 CVEs empirically verified in real VMs. 119 detection rules. ATT&CK + CWE + KEV annotated."> <meta property="og:description" content="39 Linux LPE modules; 27 of 34 CVEs empirically verified in real VMs. 151 detection rules. ATT&CK + CWE + KEV annotated.">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:url" content="https://karazajac.github.io/SKELETONKEY/"> <meta property="og:url" content="https://karazajac.github.io/SKELETONKEY/">
<meta property="og:image" content="https://karazajac.github.io/SKELETONKEY/og.png"> <meta property="og:image" content="https://karazajac.github.io/SKELETONKEY/og.png">
@@ -56,14 +56,14 @@
<div class="container hero-inner"> <div class="container hero-inner">
<div class="hero-eyebrow"> <div class="hero-eyebrow">
<span class="dot dot-pulse"></span> <span class="dot dot-pulse"></span>
v0.9.0 — released 2026-05-24 v0.9.1 — released 2026-05-24
</div> </div>
<h1 class="hero-title"> <h1 class="hero-title">
<span class="display-wordmark">SKELETONKEY</span> <span class="display-wordmark">SKELETONKEY</span>
</h1> </h1>
<p class="hero-tag"> <p class="hero-tag">
One binary. <strong>39 Linux LPE modules</strong> covering 34 CVEs — One binary. <strong>39 Linux LPE modules</strong> covering 34 CVEs —
<strong>every year 2016 → 2026</strong>. 22 of 34 confirmed against <strong>every year 2016 → 2026</strong>. 27 of 34 confirmed against
real Linux kernels in VMs. SOC-ready detection rules in four SIEM real Linux kernels in VMs. SOC-ready detection rules in four SIEM
formats. MITRE ATT&amp;CK + CWE + CISA KEV annotated. formats. MITRE ATT&amp;CK + CWE + CISA KEV annotated.
<span class="hero-tag-pop">--explain gives a one-page operator briefing per CVE.</span> <span class="hero-tag-pop">--explain gives a one-page operator briefing per CVE.</span>
@@ -82,7 +82,7 @@
<div class="stats-row" id="stats-row"> <div class="stats-row" id="stats-row">
<div class="stat-chip"><span class="num" data-target="39">0</span><span>modules</span></div> <div class="stat-chip"><span class="num" data-target="39">0</span><span>modules</span></div>
<div class="stat-chip stat-vfy"><span class="num" data-target="22">0</span><span>✓ VM-verified</span></div> <div class="stat-chip stat-vfy"><span class="num" data-target="27">0</span><span>✓ VM-verified</span></div>
<div class="stat-chip stat-kev"><span class="num" data-target="11">0</span><span>★ in CISA KEV</span></div> <div class="stat-chip stat-kev"><span class="num" data-target="11">0</span><span>★ in CISA KEV</span></div>
<div class="stat-chip"><span class="num" data-target="151">0</span><span>detection rules</span></div> <div class="stat-chip"><span class="num" data-target="151">0</span><span>detection rules</span></div>
</div> </div>
@@ -598,7 +598,7 @@ uid=0(root) gid=0(root)</pre>
who found the bugs. who found the bugs.
</p> </p>
<p class="footer-meta"> <p class="footer-meta">
v0.9.0 · MIT · <a href="https://github.com/KaraZajac/SKELETONKEY">github.com/KaraZajac/SKELETONKEY</a> v0.9.1 · MIT · <a href="https://github.com/KaraZajac/SKELETONKEY">github.com/KaraZajac/SKELETONKEY</a>
</p> </p>
</div> </div>
</footer> </footer>
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

+3 -3
View File
@@ -39,7 +39,7 @@
Curated Linux LPE corpus. Curated Linux LPE corpus.
</text> </text>
<text x="80" y="278" font-family="'Inter',sans-serif" font-size="30" fill="#c5c5d3" font-weight="500"> <text x="80" y="278" font-family="'Inter',sans-serif" font-size="30" fill="#c5c5d3" font-weight="500">
Every year 2016 → 2026. 22 of 34 verified. Every year 2016 → 2026. 27 of 34 verified.
</text> </text>
<!-- stat chips --> <!-- stat chips -->
@@ -49,9 +49,9 @@
<text x="28" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#ecedf7">39</text> <text x="28" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#ecedf7">39</text>
<text x="64" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">modules</text> <text x="64" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">modules</text>
<!-- 22 VM-verified --> <!-- 27 VM-verified -->
<rect x="206" y="0" width="240" height="58" rx="29" fill="#161628" stroke="#10b981" stroke-opacity="0.5"/> <rect x="206" y="0" width="240" height="58" rx="29" fill="#161628" stroke="#10b981" stroke-opacity="0.5"/>
<text x="234" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#34d399">22</text> <text x="234" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#34d399">27</text>
<text x="270" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">✓ VM-verified</text> <text x="270" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">✓ VM-verified</text>
<!-- 11 KEV --> <!-- 11 KEV -->

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

+1 -1
View File
@@ -35,7 +35,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#define SKELETONKEY_VERSION "0.9.0" #define SKELETONKEY_VERSION "0.9.1"
static const char BANNER[] = static const char BANNER[] =
"\n" "\n"
+42 -6
View File
@@ -73,7 +73,19 @@ Vagrant.configure("2") do |c|
echo "[+] installing #{pkg} (kernel target #{kver})" echo "[+] installing #{pkg} (kernel target #{kver})"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get install -y -qq #{pkg} apt-get install -y -qq #{pkg}
echo "[i] kernel #{pkg} installed; reboot via 'vagrant reload'" echo "[i] kernel #{pkg} installed"
fi
# Pin grub default to this specific kernel. Without it, grub
# picks the highest-versioned kernel installed (typically a
# stock HWE backport that's POST-fix), defeating the pin's
# purpose. Find the kver string by stripping linux-image-
# prefix from the pkg name.
PINNED_KVER="$(echo '#{pkg}' | sed 's/^linux-image-//')"
if [ -f "/boot/vmlinuz-${PINNED_KVER}" ]; then
GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${PINNED_KVER}"
sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub
echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}"
update-grub 2>&1 | tail -3
fi fi
SHELL SHELL
end end
@@ -90,16 +102,24 @@ Vagrant.configure("2") do |c|
m.vm.provision "shell", name: "pin-mainline-#{mainline}", inline: <<-SHELL m.vm.provision "shell", name: "pin-mainline-#{mainline}", inline: <<-SHELL
set -e set -e
KVER="#{mainline}" KVER="#{mainline}"
# already booted into it? # already booted into it? Still fall through to grub-pin to
# make sure GRUB_DEFAULT stays correct even after stock kernel
# upgrades that might reorder grub entries.
BOOTED_INTO_TARGET=0
if uname -r | grep -q "^${KVER}-[0-9]\\+-generic"; then if uname -r | grep -q "^${KVER}-[0-9]\\+-generic"; then
echo "[=] mainline ${KVER} already booted ($(uname -r))" echo "[=] mainline ${KVER} already booted ($(uname -r))"
exit 0 BOOTED_INTO_TARGET=1
fi fi
# already installed on disk (waiting on reboot)?
# already installed on disk? Skip the download/install but
# still run the grub-pin block at the end.
SKIP_INSTALL=0
if ls /boot/vmlinuz-${KVER}-* >/dev/null 2>&1; then if ls /boot/vmlinuz-${KVER}-* >/dev/null 2>&1; then
echo "[=] mainline ${KVER} already installed; needs reboot" echo "[=] mainline ${KVER} already installed on disk"
exit 0 SKIP_INSTALL=1
fi fi
if [ "$SKIP_INSTALL" -eq 0 ]; then
echo "[+] fetching kernel.ubuntu.com mainline v${KVER}" echo "[+] fetching kernel.ubuntu.com mainline v${KVER}"
# Newer mainline kernels live under /v${KVER}/amd64/; older ones # Newer mainline kernels live under /v${KVER}/amd64/; older ones
# (≤ ~4.15) put debs at /v${KVER}/ directly. Try /amd64/ first; # (≤ ~4.15) put debs at /v${KVER}/ directly. Try /amd64/ first;
@@ -131,6 +151,22 @@ Vagrant.configure("2") do |c|
done done
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
dpkg -i *.deb || apt-get install -f -y -qq dpkg -i *.deb || apt-get install -f -y -qq
fi # end SKIP_INSTALL guard
# Pin grub default to the just-installed mainline kernel. Without
# this, grub's debian-version-compare picks the highest-sorting
# vmlinuz-* as default; for downgrades (e.g. stock 4.15 → mainline
# 4.14.70), the OLD kernel wins because 4.15 > 4.14 numerically.
MAINLINE_VMLINUZ=$(ls /boot/vmlinuz-${KVER}-* 2>/dev/null | head -1)
if [ -n "$MAINLINE_VMLINUZ" ]; then
MAINLINE_KVER=$(basename "$MAINLINE_VMLINUZ" | sed 's/^vmlinuz-//')
# The "Advanced options" submenu entry id is stable across
# update-grub runs as "gnulinux-advanced-<UUID>>gnulinux-<kver>-advanced-<UUID>".
# Easier: use the human menuentry path.
GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${MAINLINE_KVER}"
sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub
echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}"
fi
update-grub 2>&1 | tail -3 update-grub 2>&1 | tail -3
echo "[i] mainline ${KVER} installed; reboot via 'vagrant reload'" echo "[i] mainline ${KVER} installed; reboot via 'vagrant reload'"
SHELL SHELL
@@ -9,7 +9,7 @@
set -e set -e
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get install -y -qq udisks2 libblockdev-utils3 >/dev/null apt-get install -y -qq udisks2 libblockdev-utils2 >/dev/null
mkdir -p /etc/polkit-1/rules.d mkdir -p /etc/polkit-1/rules.d
cat >/etc/polkit-1/rules.d/49-skk-verify.rules <<'EOF' cat >/etc/polkit-1/rules.d/49-skk-verify.rules <<'EOF'
@@ -31,4 +31,4 @@ sleep 2
echo "[+] udisks2 status:" echo "[+] udisks2 status:"
systemctl is-active udisks2.service systemctl is-active udisks2.service
echo "[+] udisks2 version: $(dpkg-query -W -f='${Version}' udisks2)" echo "[+] udisks2 version: $(dpkg-query -W -f='${Version}' udisks2)"
echo "[+] libblockdev version: $(dpkg-query -W -f='${Version}' libblockdev-utils3 2>/dev/null || dpkg-query -W -f='${Version}' libblockdev-utils2)" echo "[+] libblockdev version: $(dpkg-query -W -f='${Version}' libblockdev-utils2)"
+9 -8
View File
@@ -248,12 +248,12 @@ pintheft:
# ── v0.9.0 additions (gap fillers 2018 / 2019 / 2020 / 2024) ────── # ── v0.9.0 additions (gap fillers 2018 / 2019 / 2020 / 2024) ──────
mutagen_astronomy: mutagen_astronomy:
box: ubuntu1804 # stock 4.15.0-213 is post-fix; mainline 4.14.70 is one below the .71 fix box: ""
kernel_pkg: "" kernel_pkg: ""
mainline_version: "4.14.70" kernel_version: ""
kernel_version: "4.14.70" expect_detect: ""
expect_detect: VULNERABLE manual: true
notes: "CVE-2018-14634; Qualys Mutagen Astronomy. Mainline 4.14.70 sits one stable below the 4.14.71 backport. Old mainline kernels live at /v${KVER}/ directly (no /amd64/ subdir); Vagrantfile's pin-mainline provisioner falls back to the bare path." notes: "CVE-2018-14634; Qualys Mutagen Astronomy. No good Vagrant verification environment: stock Ubuntu 18.04 (4.15.0-213) returns detect()=VULNERABLE because the module's kernel_range table has no entry for the 4.15.x series (Ubuntu's HWE backports are not modeled), but the kernel IS actually patched — false-positive of the conservative module logic. Mainline 4.14.70 (target VULNERABLE kernel) panics on Ubuntu 18.04's rootfs with 'Failed to execute /init (error -8)' — kernel config mismatch (binfmt_elf as module rather than baked-in). Genuinely vulnerable verification needs a contemporary CentOS 6 / Debian 7 image with original-vintage kernel; deferred to custom-box workflow."
sudo_runas_neg1: sudo_runas_neg1:
box: ubuntu1804 # ships sudo 1.8.21p2 (vulnerable; pre-1.8.28 fix) box: ubuntu1804 # ships sudo 1.8.21p2 (vulnerable; pre-1.8.28 fix)
@@ -279,7 +279,8 @@ vsock_uaf:
nft_pipapo: nft_pipapo:
box: ubuntu2204 # 5.15 stock + HWE — same pipapo set substrate as nf_tables box: ubuntu2204 # 5.15 stock + HWE — same pipapo set substrate as nf_tables
kernel_pkg: linux-image-5.15.0-43-generic kernel_pkg: ""
kernel_version: "5.15.0-43" mainline_version: "5.15.5"
kernel_version: "5.15.5"
expect_detect: VULNERABLE expect_detect: VULNERABLE
notes: "CVE-2024-26581; nft_pipapo destroy-race (Notselwyn II). Same Vagrant target as nf_tables works here — stock 5.15.0-43 is below the 5.15.149 backport. Userns gate must be open (sysctl kernel.unprivileged_userns_clone=1)." notes: "CVE-2024-26581; nft_pipapo destroy-race (Notselwyn II). Same mainline 5.15.5 target as nf_tables works here — 5.15.5 is below the 5.15.149 backport. (Switched from apt-pinned 5.15.0-43 after that package was removed from Ubuntu repos.) Userns gate must be open (sysctl kernel.unprivileged_userns_clone=1)."
+39 -14
View File
@@ -139,19 +139,6 @@ if ! vagrant status "$VM_HOSTNAME" 2>&1 | grep -q "running"; then
vagrant up "$VM_HOSTNAME" --provider=parallels vagrant up "$VM_HOSTNAME" --provider=parallels
fi fi
# Reboot if any kernel pin was applied (uname -r != target).
if [[ -n "$KERNEL_PKG" || -n "$MAINLINE" ]]; then
current_kver=$(vagrant ssh "$VM_HOSTNAME" -c "uname -r" 2>/dev/null | tr -d '\r')
target_match="$KERNEL_VER"
[[ -n "$MAINLINE" ]] && target_match="$MAINLINE"
if [[ "$current_kver" != *"$target_match"* ]]; then
echo "[*] current kernel $current_kver != target $target_match; rebooting..."
vagrant reload "$VM_HOSTNAME"
sleep 5
fi
fi
# Run the explain probe.
LOG="$LOG_DIR/verify-${MODULE}-$(date +%Y%m%d-%H%M%S).log" LOG="$LOG_DIR/verify-${MODULE}-$(date +%Y%m%d-%H%M%S).log"
# Force rsync the source tree in. vagrant up runs rsync automatically on # Force rsync the source tree in. vagrant up runs rsync automatically on
@@ -160,8 +147,46 @@ LOG="$LOG_DIR/verify-${MODULE}-$(date +%Y%m%d-%H%M%S).log"
echo "[*] syncing source into VM..." echo "[*] syncing source into VM..."
vagrant rsync "$VM_HOSTNAME" 2>&1 | tail -5 vagrant rsync "$VM_HOSTNAME" 2>&1 | tail -5
# Two-phase provisioning so the new kernel actually boots before verify:
# PREP: install kernel (apt or mainline) + pin grub default + run any
# module-specific provisioner (sudoers grant, sudo build, ...).
# ── conditional reboot if uname -r doesn't match target ──
# VERIFY: build skeletonkey + run --explain --active.
PREP_PROVS=()
[[ -n "$KERNEL_PKG" ]] && PREP_PROVS+=("pin-kernel-${KERNEL_PKG}")
[[ -n "$MAINLINE" ]] && PREP_PROVS+=("pin-mainline-${MAINLINE}")
[[ -f "$VM_DIR/provisioners/${MODULE}.sh" ]] && PREP_PROVS+=("module-provision-${MODULE}")
if [[ ${#PREP_PROVS[@]} -gt 0 ]]; then
echo "[*] running prep provisioners: ${PREP_PROVS[*]}"
vagrant provision "$VM_HOSTNAME" \
--provision-with "$(IFS=,; echo "${PREP_PROVS[*]}")" 2>&1 | tee "$LOG"
fi
# Reboot if a kernel pin moved us off the target. This must run AFTER
# the prep provisioners (which install the kernel + set GRUB_DEFAULT),
# otherwise the reboot picks the stock kernel and we never land on the
# target.
if [[ -n "$KERNEL_PKG" || -n "$MAINLINE" ]]; then
current_kver=$(vagrant ssh "$VM_HOSTNAME" -c "uname -r" 2>/dev/null | tr -d '\r')
target_match="$KERNEL_VER"
[[ -n "$MAINLINE" ]] && target_match="$MAINLINE"
if [[ "$current_kver" != *"$target_match"* ]]; then
echo "[*] current kernel $current_kver != target $target_match; rebooting..."
vagrant reload "$VM_HOSTNAME" 2>&1 | tee -a "$LOG"
sleep 5
post_kver=$(vagrant ssh "$VM_HOSTNAME" -c "uname -r" 2>/dev/null | tr -d '\r')
echo "[*] post-reboot kernel: $post_kver" | tee -a "$LOG"
if [[ "$post_kver" != *"$target_match"* ]]; then
echo "[!] reboot did NOT land on target kernel $target_match (got $post_kver)" | tee -a "$LOG"
echo " detect() will still run, but verification is on the wrong kernel" | tee -a "$LOG"
fi
fi
fi
echo "[*] running verifier..." echo "[*] running verifier..."
vagrant provision "$VM_HOSTNAME" --provision-with build-and-verify 2>&1 | tee "$LOG" vagrant provision "$VM_HOSTNAME" \
--provision-with build-and-verify 2>&1 | tee -a "$LOG"
# Parse verdict. Vagrant prefixes provisioner output with the VM name # Parse verdict. Vagrant prefixes provisioner output with the VM name
# (e.g. " skk-pwnkit: VERDICT: VULNERABLE"), so anchor on the VERDICT # (e.g. " skk-pwnkit: VERDICT: VULNERABLE"), so anchor on the VERDICT