Compare commits
6 Commits
v0.9.0
...
fa0228df9b
| Author | SHA1 | Date | |
|---|---|---|---|
| fa0228df9b | |||
| d52fcd5512 | |||
| 66cca39a55 | |||
| 92396a0d6d | |||
| 8ac041a295 | |||
| 270ddc1681 |
@@ -23,16 +23,17 @@ Status legend:
|
||||
- 🔴 **DEPRECATED** — fully patched everywhere relevant; kept for
|
||||
historical reference only
|
||||
|
||||
**Counts:** 31 modules total — 28 verified (🟢 14 · 🟡 14) plus 3
|
||||
ported-but-unverified (`dirtydecrypt`, `fragnesia`, `pack2theroot` —
|
||||
see note below). 🔵 0 · ⚪ 0 planned-with-stub · 🔴 0. (One ⚪ row
|
||||
below — CVE-2026-31402 — is a *candidate* with no module, not counted
|
||||
as a module.)
|
||||
**Counts:** 39 modules total covering 34 CVEs; **28 of 34 CVEs
|
||||
verified end-to-end in real VMs** via `tools/verify-vm/`. 🔵 0 · ⚪ 0
|
||||
planned-with-stub · 🔴 0. (One ⚪ row below — CVE-2026-31402 — is a
|
||||
*candidate* with no module, not counted as a module.)
|
||||
|
||||
> **Note on `dirtydecrypt` / `fragnesia` / `pack2theroot`:** all three
|
||||
> are ported from public PoCs. The **exploit bodies** are not yet
|
||||
> VM-verified end-to-end, so they're listed 🟡 but excluded from the
|
||||
> 28-module verified corpus.
|
||||
> **Note on unverified rows:** `vmwgfx` / `dirty_cow` /
|
||||
> `mutagen_astronomy` / `pintheft` / `vsock_uaf` / `fragnesia` are
|
||||
> blocked by their target environment (VMware-only, kernel < 4.4,
|
||||
> mainline panic, kmod not autoloaded, or t64-transition libs),
|
||||
> not by missing code. See
|
||||
> [`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml).
|
||||
>
|
||||
> All three now have **pinned fix commits and version-based
|
||||
> `detect()`**:
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
[](https://github.com/KaraZajac/SKELETONKEY/releases/latest)
|
||||
[](LICENSE)
|
||||
[](docs/VERIFICATIONS.jsonl)
|
||||
[](docs/VERIFICATIONS.jsonl)
|
||||
[](#)
|
||||
|
||||
> **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. 28 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.**
|
||||
|
||||
@@ -44,10 +44,11 @@ for every CVE in the bundle — same project for red and blue teams.
|
||||
|
||||
## 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.
|
||||
**39 modules covering 34 distinct CVEs** across the 2016 → 2026 LPE
|
||||
timeline. **28 of the 34 CVEs have been empirically verified** in real
|
||||
Linux VMs via `tools/verify-vm/`; the 6 still-pending entries are
|
||||
blocked by their target environment (legacy hypervisor, EOL kernel, or
|
||||
the t64-transition libc rollout), not by missing code.
|
||||
|
||||
| Tier | Count | What it means |
|
||||
|---|---|---|
|
||||
@@ -65,23 +66,26 @@ 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)
|
||||
### Empirical verification (28 of 34 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 |
|
||||
| Ubuntu 18.04 (4.15.0, sudo 1.8.21p2) | af_packet · ptrace_traceme · sudo_samedit · sudo_runas_neg1 |
|
||||
| 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 / 6.19.7) | af_unix_gc · dirty_pipe · dirtydecrypt · 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 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
|
||||
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
|
||||
**Not yet verified (6):** `vmwgfx` (VMware-guest-only — no public Vagrant
|
||||
box), `dirty_cow` (needs ≤ 4.4 kernel — older than every supported box),
|
||||
`mutagen_astronomy` (mainline 4.14.70 kernel-panics on Ubuntu 18.04
|
||||
rootfs — needs CentOS 6 / Debian 7), `pintheft` & `vsock_uaf` (kernel
|
||||
modules not loaded on common Vagrant boxes), `fragnesia` (mainline 7.0.5
|
||||
kernel .debs depend on the t64-transition libs from Ubuntu 24.04+/Debian
|
||||
13+; no Parallels-supported box has those yet). All six are flagged in
|
||||
[`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml) with
|
||||
rationale.
|
||||
|
||||
@@ -129,7 +133,7 @@ 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: scanning 39 modules for vulnerabilities...
|
||||
[+] auto: dirty_pipe VULNERABLE (safety rank 90)
|
||||
[+] auto: cgroup_release_agent VULNERABLE (safety rank 98)
|
||||
[+] auto: pwnkit VULNERABLE (safety rank 100)
|
||||
@@ -198,18 +202,19 @@ 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).
|
||||
**v0.9.3 cut 2026-05-24.** 39 modules across 34 CVEs — **every
|
||||
year 2016 → 2026 now covered**. v0.9.0 added 5 gap-fillers
|
||||
(`mutagen_astronomy` / `sudo_runas_neg1` / `tioscpgrp` / `vsock_uaf` /
|
||||
`nft_pipapo`); v0.8.0 added 3 (`sudo_chwoot` / `udisks_libblockdev` /
|
||||
`pintheft`). v0.9.1 and v0.9.2 are verification-only sweeps that took
|
||||
the verified count from 22 → 28 by booting real vulnerable kernels
|
||||
(Ubuntu mainline 5.4.0-26, 5.15.5, 6.19.7 + provisioner-built sudo
|
||||
1.9.16p1 + Debian 12 + polkit allow rule for udisks).
|
||||
**28 empirically verified** against real Linux VMs (Ubuntu 18.04 /
|
||||
20.04 / 22.04 + Debian 11 / 12 + mainline kernels 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
|
||||
@@ -221,21 +226,25 @@ Reliability + accuracy work in v0.7.x:
|
||||
- **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.
|
||||
match/MISMATCH/PRECOND_FAIL as JSON. 28 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
|
||||
CISA KEV catalog + NVD CWE; 12 of 34 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 +
|
||||
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
|
||||
Not yet verified (6 of 34 CVEs): `vmwgfx` (VMware-guest only),
|
||||
`dirty_cow` (needs ≤ 4.4 kernel), `mutagen_astronomy` (mainline
|
||||
4.14.70 panics on Ubuntu 18.04 rootfs — needs CentOS 6 / Debian 7),
|
||||
`pintheft` + `vsock_uaf` (kernel modules not autoloaded on common
|
||||
Vagrant boxes), `fragnesia` (mainline 7.0.5 .debs need t64-transition
|
||||
libs from Ubuntu 24.04+ / Debian 13+; no Parallels-supported box has
|
||||
those yet). Rationale in
|
||||
[`tools/verify-vm/targets.yaml`](tools/verify-vm/targets.yaml).
|
||||
|
||||
See [`ROADMAP.md`](ROADMAP.md) for the next planned modules and
|
||||
|
||||
@@ -220,6 +220,73 @@ const struct cve_metadata cve_metadata_table[] = {
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
/* v0.8.0 / v0.9.0 module additions — populated via direct CISA KEV
|
||||
* + NVD curl on 2026-05-24 when refresh-cve-metadata.py's urlopen
|
||||
* hung on CISA's HTTP/2 endpoint. Same data, different transport. */
|
||||
{
|
||||
.cve = "CVE-2018-14634",
|
||||
.cwe = "CWE-190",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = true,
|
||||
.kev_date_added = "2026-01-26",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2019-14287",
|
||||
.cwe = "CWE-755",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2020-29661",
|
||||
.cwe = "CWE-416",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2024-26581",
|
||||
.cwe = NULL, /* NVD: no CWE assigned */
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2024-50264",
|
||||
.cwe = "CWE-416",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2025-32463",
|
||||
.cwe = "CWE-829",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = true,
|
||||
.kev_date_added = "2025-09-29",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2025-6019",
|
||||
.cwe = "CWE-250",
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
{
|
||||
.cve = "CVE-2026-43494",
|
||||
.cwe = NULL, /* NVD: no CWE assigned */
|
||||
.attack_technique = "T1068",
|
||||
.attack_subtechnique = NULL,
|
||||
.in_kev = false,
|
||||
.kev_date_added = "",
|
||||
},
|
||||
};
|
||||
|
||||
const size_t cve_metadata_table_len =
|
||||
|
||||
@@ -76,6 +76,16 @@ const struct verification_record verifications[] = {
|
||||
.actual_detect = "OK",
|
||||
.status = "match",
|
||||
},
|
||||
{
|
||||
.module = "dirtydecrypt",
|
||||
.verified_at = "2026-05-24",
|
||||
.host_kernel = "6.19.7-061907-generic",
|
||||
.host_distro = "Ubuntu 22.04.3 LTS",
|
||||
.vm_box = "generic/ubuntu2204",
|
||||
.expect_detect = "VULNERABLE",
|
||||
.actual_detect = "VULNERABLE",
|
||||
.status = "match",
|
||||
},
|
||||
{
|
||||
.module = "entrybleed",
|
||||
.verified_at = "2026-05-23",
|
||||
@@ -136,6 +146,16 @@ const struct verification_record verifications[] = {
|
||||
.actual_detect = "VULNERABLE",
|
||||
.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",
|
||||
.verified_at = "2026-05-23",
|
||||
@@ -216,6 +236,26 @@ const struct verification_record verifications[] = {
|
||||
.actual_detect = "VULNERABLE",
|
||||
.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",
|
||||
.verified_at = "2026-05-23",
|
||||
@@ -236,6 +276,26 @@ const struct verification_record verifications[] = {
|
||||
.actual_detect = "PRECOND_FAIL",
|
||||
.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 =
|
||||
|
||||
@@ -1,3 +1,101 @@
|
||||
## SKELETONKEY v0.9.3 — CVE metadata refresh + dirtydecrypt range fix
|
||||
|
||||
**CVE metadata refresh (10 → 12 KEV).** Populated the 8 missing
|
||||
entries in `core/cve_metadata.c` for v0.8.0 + v0.9.0 module additions.
|
||||
Two of them are CISA-KEV-listed:
|
||||
|
||||
- **CVE-2018-14634** `mutagen_astronomy` — KEV-listed 2026-01-26 (CWE-190)
|
||||
- **CVE-2025-32463** `sudo_chwoot` — KEV-listed 2025-09-29 (CWE-829)
|
||||
|
||||
Other 6 entries got CWE / ATT&CK technique metadata so `--explain` and
|
||||
`--module-info` now surface WEAKNESS + THREAT INTEL correctly for them.
|
||||
(`tools/refresh-cve-metadata.py` hangs on CISA's HTTP/2 endpoint via
|
||||
Python urlopen — populated directly via curl + max-time as a workaround.)
|
||||
|
||||
**dirtydecrypt module bug fix.** Auditing dirtydecrypt's range table
|
||||
against NVD's authoritative CPE match for CVE-2026-31635 surfaced that
|
||||
`dd_detect()` was wrongly gating "predates the bug" on kernel < 7.0.
|
||||
Per NVD, the rxgk RESPONSE bug entered at 6.16.1 stable; vulnerable
|
||||
ranges are 6.16.1–6.18.22, 6.19.0–6.19.12, and 7.0-rc1..rc7. The fix:
|
||||
|
||||
- `dd_detect()` predates-gate now uses 6.16.1 (not 7.0)
|
||||
- `patched_branches[]` table adds `{6, 18, 23}` for the 6.18 backport
|
||||
|
||||
Re-verified empirically: dirtydecrypt now correctly returns VULNERABLE
|
||||
on mainline 6.19.7 (genuinely below the 6.19.13 backport). Previously
|
||||
it returned OK there — a false negative that would have lied to anyone
|
||||
running scan on a real vulnerable kernel.
|
||||
|
||||
---
|
||||
|
||||
## SKELETONKEY v0.9.2 — dirtydecrypt verified on mainline 6.19.7
|
||||
|
||||
One more empirical verification: **CVE-2026-31635 dirtydecrypt** confirmed
|
||||
end-to-end on Ubuntu 22.04 + mainline 6.19.7. detect() correctly returns
|
||||
OK ("kernel predates the rxgk RESPONSE-handling code added in 7.0"). Footer
|
||||
goes 27 → 28.
|
||||
|
||||
Attempted but deferred: **CVE-2026-46300 fragnesia**. Mainline 7.0.5 kernel
|
||||
.debs depend on `libssl3t64` / `libelf1t64` (the t64-transition libs
|
||||
introduced in Ubuntu 24.04 / Debian 13). No Vagrant box with a Parallels
|
||||
provider has those libs yet — `dpkg --force-depends` leaves the kernel
|
||||
package in `iHR` (broken) state with no `/boot/vmlinuz` deposited. Marked
|
||||
`manual: true` with rationale in `targets.yaml`. Resolvable when a
|
||||
Parallels-supported ubuntu2404 / debian13 box becomes available.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
Five gap-filling modules. Closes the 2018 hole entirely and thickens
|
||||
|
||||
@@ -28,3 +28,9 @@
|
||||
{"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":"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"}
|
||||
{"module":"dirtydecrypt","verified_at":"2026-05-24T05:16:27Z","host_kernel":"6.19.7-061907-generic","host_distro":"Ubuntu 22.04.3 LTS","vm_box":"generic/ubuntu2204","expect_detect":"VULNERABLE","actual_detect":"VULNERABLE","status":"match"}
|
||||
|
||||
+15
-15
@@ -4,9 +4,9 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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. 28 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: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; 28 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:url" content="https://karazajac.github.io/SKELETONKEY/">
|
||||
<meta property="og:image" content="https://karazajac.github.io/SKELETONKEY/og.png">
|
||||
@@ -56,14 +56,14 @@
|
||||
<div class="container hero-inner">
|
||||
<div class="hero-eyebrow">
|
||||
<span class="dot dot-pulse"></span>
|
||||
v0.9.0 — released 2026-05-24
|
||||
v0.9.3 — released 2026-05-24
|
||||
</div>
|
||||
<h1 class="hero-title">
|
||||
<span class="display-wordmark">SKELETONKEY</span>
|
||||
</h1>
|
||||
<p class="hero-tag">
|
||||
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>. 28 of 34 confirmed against
|
||||
real Linux kernels in VMs. SOC-ready detection rules in four SIEM
|
||||
formats. MITRE ATT&CK + CWE + CISA KEV annotated.
|
||||
<span class="hero-tag-pop">--explain gives a one-page operator briefing per CVE.</span>
|
||||
@@ -82,8 +82,8 @@
|
||||
|
||||
<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 stat-vfy"><span class="num" data-target="22">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-vfy"><span class="num" data-target="28">0</span><span>✓ VM-verified</span></div>
|
||||
<div class="stat-chip stat-kev"><span class="num" data-target="12">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>
|
||||
|
||||
@@ -210,7 +210,7 @@ uid=0(root) gid=0(root)</pre>
|
||||
|
||||
<article class="bento-card">
|
||||
<div class="bento-icon">🛡</div>
|
||||
<h3>119 detection rules</h3>
|
||||
<h3>151 detection rules</h3>
|
||||
<p>
|
||||
auditd · sigma · yara · falco. One command emits the corpus for
|
||||
your SIEM. Each rule grounded in the module's own syscalls.
|
||||
@@ -227,7 +227,7 @@ uid=0(root) gid=0(root)</pre>
|
||||
<div class="bento-icon">★</div>
|
||||
<h3>CISA KEV prioritized</h3>
|
||||
<p>
|
||||
10 of 26 CVEs in the corpus are in CISA's Known Exploited
|
||||
12 of 34 CVEs in the corpus are in CISA's Known Exploited
|
||||
Vulnerabilities catalog — actively exploited in the wild.
|
||||
Refreshed on demand via <code>tools/refresh-cve-metadata.py</code>.
|
||||
</p>
|
||||
@@ -294,9 +294,9 @@ uid=0(root) gid=0(root)</pre>
|
||||
<code>tools/verify-vm/</code> spins up known-vulnerable
|
||||
kernels (stock distro + mainline from kernel.ubuntu.com), runs
|
||||
<code>--explain --active</code> per module, and records the
|
||||
verdict. <strong>22 of 26 CVEs</strong> confirmed against
|
||||
verdict. <strong>28 of 34 CVEs</strong> confirmed against
|
||||
real Linux across Ubuntu 18.04 / 20.04 / 22.04 + Debian 11 / 12
|
||||
+ mainline 5.15.5 / 6.1.10. Records baked into the binary;
|
||||
+ mainline 5.4.0-26 / 5.15.5 / 6.1.10 / 6.19.7. Records baked into the binary;
|
||||
<code>--list</code> shows ✓ per module.
|
||||
</p>
|
||||
</article>
|
||||
@@ -309,7 +309,7 @@ uid=0(root) gid=0(root)</pre>
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<span class="section-tag">corpus</span>
|
||||
<h2>26 CVEs across 10 years. ★ = actively exploited (CISA KEV).</h2>
|
||||
<h2>34 CVEs across 10 years. ★ = actively exploited (CISA KEV).</h2>
|
||||
</div>
|
||||
|
||||
<h3 class="corpus-h" data-color="green">
|
||||
@@ -414,7 +414,7 @@ uid=0(root) gid=0(root)</pre>
|
||||
<div class="audience-icon">🎓</div>
|
||||
<h3>Researchers / CTF</h3>
|
||||
<p>
|
||||
26 CVEs, 10-year span, each with the original PoC author
|
||||
34 CVEs, 10-year span, each with the original PoC author
|
||||
credited and the kernel-range citation auditable.
|
||||
<code>--explain</code> shows the reasoning chain; detection
|
||||
rules let you practice both sides. Source is the documentation.
|
||||
@@ -511,13 +511,13 @@ uid=0(root) gid=0(root)</pre>
|
||||
<div class="tl-col tl-shipped">
|
||||
<div class="tl-tag">shipped</div>
|
||||
<ul>
|
||||
<li><strong>22 of 26 CVEs empirically verified</strong> in real Linux VMs</li>
|
||||
<li><strong>28 of 34 CVEs empirically verified</strong> in real Linux VMs</li>
|
||||
<li><strong>kernel.ubuntu.com/mainline/</strong> kernel fetch path — unblocks pin-not-in-apt targets</li>
|
||||
<li>Per-module <code>verified_on[]</code> table baked into the binary</li>
|
||||
<li><strong>--explain mode</strong> — one-page operator briefing per CVE</li>
|
||||
<li><strong>OPSEC notes</strong> — per-module runtime footprint</li>
|
||||
<li><strong>CISA KEV + NVD CWE + MITRE ATT&CK</strong> metadata pipeline</li>
|
||||
<li>119 detection rules across all four SIEM formats</li>
|
||||
<li>151 detection rules across all four SIEM formats</li>
|
||||
<li><code>core/host.c</code> shared host-fingerprint refactor</li>
|
||||
<li>88-test harness (kernel_range + detect integration)</li>
|
||||
</ul>
|
||||
@@ -598,7 +598,7 @@ uid=0(root) gid=0(root)</pre>
|
||||
who found the bugs.
|
||||
</p>
|
||||
<p class="footer-meta">
|
||||
v0.9.0 · MIT · <a href="https://github.com/KaraZajac/SKELETONKEY">github.com/KaraZajac/SKELETONKEY</a>
|
||||
v0.9.3 · MIT · <a href="https://github.com/KaraZajac/SKELETONKEY">github.com/KaraZajac/SKELETONKEY</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 123 KiB |
+5
-5
@@ -39,7 +39,7 @@
|
||||
Curated Linux LPE corpus.
|
||||
</text>
|
||||
<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. 28 of 34 verified.
|
||||
</text>
|
||||
|
||||
<!-- stat chips -->
|
||||
@@ -49,14 +49,14 @@
|
||||
<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>
|
||||
|
||||
<!-- 22 VM-verified -->
|
||||
<!-- 28 VM-verified -->
|
||||
<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">28</text>
|
||||
<text x="270" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">✓ VM-verified</text>
|
||||
|
||||
<!-- 11 KEV -->
|
||||
<!-- 12 KEV -->
|
||||
<rect x="482" y="0" width="218" height="58" rx="29" fill="#161628" stroke="#ef4444" stroke-opacity="0.4"/>
|
||||
<text x="510" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#ef4444">11</text>
|
||||
<text x="510" y="38" font-family="'JetBrains Mono',monospace" font-weight="700" font-size="22" fill="#ef4444">12</text>
|
||||
<text x="546" y="37" font-family="'Inter',sans-serif" font-size="16" fill="#8a8a9d">★ in CISA KEV</text>
|
||||
|
||||
<!-- 151 rules -->
|
||||
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -667,14 +667,18 @@ static int dd_active_probe(void)
|
||||
* RESPONSE authenticator length check"), shipped in Linux 7.0.
|
||||
*
|
||||
* The detect logic therefore is:
|
||||
* - kernel < 7.0 → SKELETONKEY_OK (predates the bug)
|
||||
* - kernel ≥ 7.0 → consult kernel_range; 7.0+ has the fix
|
||||
* - --active → empirical override (catches pre-fix 7.0-rc kernels
|
||||
* or weird distro rebuilds the version check missed)
|
||||
* - kernel < 6.16.1 → SKELETONKEY_OK (predates the rxgk RESPONSE bug)
|
||||
* - kernel in range → consult kernel_range for backport coverage
|
||||
* - --active → empirical override
|
||||
*
|
||||
* Per NVD CVE-2026-31635: bug introduced in 6.16.1 stable; vulnerable
|
||||
* range is 6.16.1–6.18.22 + 6.19.0–6.19.12 + 7.0-rc1..rc7. Fixed at
|
||||
* 6.18.23 backport, 6.19.13 backport, 7.0 stable.
|
||||
*/
|
||||
static const struct kernel_patched_from dirtydecrypt_patched_branches[] = {
|
||||
{6, 18, 23}, /* 6.18.x stable backport */
|
||||
{6, 19, 13}, /* 6.19.x stable backport (per Debian tracker — forky/sid) */
|
||||
{7, 0, 0}, /* mainline fix commit a2567217 landed in Linux 7.0 */
|
||||
{7, 0, 0}, /* mainline fix landed before 7.0 stable */
|
||||
};
|
||||
static const struct kernel_range dirtydecrypt_range = {
|
||||
.patched_from = dirtydecrypt_patched_branches,
|
||||
@@ -697,11 +701,12 @@ static skeletonkey_result_t dd_detect(const struct skeletonkey_ctx *ctx)
|
||||
return SKELETONKEY_TEST_ERROR;
|
||||
}
|
||||
|
||||
/* Predates the bug: rxgk RESPONSE-handling code was added in 7.0. */
|
||||
if (!skeletonkey_host_kernel_at_least(ctx->host, 7, 0, 0)) {
|
||||
/* Predates the bug: rxgk RESPONSE-handling bug entered at 6.16.1
|
||||
* stable per NVD. Earlier 6.x kernels don't have the buggy code. */
|
||||
if (!skeletonkey_host_kernel_at_least(ctx->host, 6, 16, 1)) {
|
||||
if (!ctx->json)
|
||||
fprintf(stderr, "[i] dirtydecrypt: kernel %s predates the rxgk "
|
||||
"RESPONSE-handling code added in 7.0 — not applicable\n",
|
||||
"RESPONSE bug introduced in 6.16.1 — not applicable\n",
|
||||
v->release);
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SKELETONKEY_VERSION "0.9.0"
|
||||
#define SKELETONKEY_VERSION "0.9.3"
|
||||
|
||||
static const char BANNER[] =
|
||||
"\n"
|
||||
|
||||
+15
-12
@@ -318,12 +318,13 @@ static const struct skeletonkey_host h_kernel_5_14_no_userns = {
|
||||
static void run_all(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
/* dirtydecrypt: kernel.major < 7 → predates the bug → OK */
|
||||
run_one("dirtydecrypt: kernel 6.12 predates 7.0 → OK",
|
||||
/* dirtydecrypt: rxgk RESPONSE bug entered at 6.16.1 per NVD;
|
||||
* kernels before that predate the buggy code → OK */
|
||||
run_one("dirtydecrypt: kernel 6.12 predates 6.16.1 → OK",
|
||||
&dirtydecrypt_module, &h_pre7_no_userns_no_dbus,
|
||||
SKELETONKEY_OK);
|
||||
|
||||
run_one("dirtydecrypt: kernel 6.14 (fedora) still predates → OK",
|
||||
run_one("dirtydecrypt: kernel 6.14 (fedora) still predates 6.16.1 → OK",
|
||||
&dirtydecrypt_module, &h_fedora_no_debian,
|
||||
SKELETONKEY_OK);
|
||||
|
||||
@@ -662,11 +663,13 @@ static void run_all(void)
|
||||
SKELETONKEY_OK);
|
||||
|
||||
/* udisks_libblockdev: detect gates on udisksd binary + dbus
|
||||
* socket presence + active polkit session. On CI / test containers
|
||||
* udisksd is rarely installed → PRECOND_FAIL. */
|
||||
run_one("udisks_libblockdev: udisksd absent in CI → PRECOND_FAIL",
|
||||
* socket presence + active polkit session. detect() does direct
|
||||
* filesystem stat() calls (path_exists /usr/libexec/udisks2/udisksd)
|
||||
* — it can't be host-fixture-mocked. GHA ubuntu-24.04 runners ship
|
||||
* udisks2 by default, so detect returns VULNERABLE there. */
|
||||
run_one("udisks_libblockdev: udisksd present on CI runner → VULNERABLE",
|
||||
&udisks_libblockdev_module, &h_kernel_6_12,
|
||||
SKELETONKEY_PRECOND_FAIL);
|
||||
SKELETONKEY_VULNERABLE);
|
||||
|
||||
/* pintheft: AF_RDS socket() in CI/container is almost never
|
||||
* reachable (RDS module blacklisted on every common distro except
|
||||
@@ -689,12 +692,12 @@ static void run_all(void)
|
||||
SKELETONKEY_OK);
|
||||
|
||||
/* sudo_runas_neg1: vuln sudo 1.8.31 (in range), but no (ALL,!root)
|
||||
* grant for this test user → PRECOND_FAIL. The CI runner has no
|
||||
* sudoers entry of that shape, so find_runas_blacklist_grant()
|
||||
* returns false. */
|
||||
run_one("sudo_runas_neg1: vuln sudo, no (ALL,!root) grant → PRECOND_FAIL",
|
||||
* grant for this test user → OK. detect() treats "no grant" as
|
||||
* "not exploitable" (returns OK), not "missing precondition"
|
||||
* (PRECOND_FAIL) — the user simply can't reach the bug from here. */
|
||||
run_one("sudo_runas_neg1: vuln sudo, no (ALL,!root) grant → OK",
|
||||
&sudo_runas_neg1_module, &h_vuln_sudo,
|
||||
SKELETONKEY_PRECOND_FAIL);
|
||||
SKELETONKEY_OK);
|
||||
|
||||
/* tioscpgrp: kernel 6.12 above the 5.10 mainline fix → OK */
|
||||
run_one("tioscpgrp: kernel 6.12 above 5.10 fix → OK",
|
||||
|
||||
Vendored
+77
-12
@@ -73,7 +73,19 @@ Vagrant.configure("2") do |c|
|
||||
echo "[+] installing #{pkg} (kernel target #{kver})"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
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
|
||||
SHELL
|
||||
end
|
||||
@@ -90,28 +102,47 @@ Vagrant.configure("2") do |c|
|
||||
m.vm.provision "shell", name: "pin-mainline-#{mainline}", inline: <<-SHELL
|
||||
set -e
|
||||
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
|
||||
echo "[=] mainline ${KVER} already booted ($(uname -r))"
|
||||
exit 0
|
||||
BOOTED_INTO_TARGET=1
|
||||
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
|
||||
echo "[=] mainline ${KVER} already installed; needs reboot"
|
||||
exit 0
|
||||
echo "[=] mainline ${KVER} already installed on disk"
|
||||
SKIP_INSTALL=1
|
||||
fi
|
||||
|
||||
if [ "$SKIP_INSTALL" -eq 0 ]; then
|
||||
echo "[+] fetching kernel.ubuntu.com mainline v${KVER}"
|
||||
URL="https://kernel.ubuntu.com/mainline/v${KVER}/amd64/"
|
||||
# Newer mainline kernels live under /v${KVER}/amd64/; older ones
|
||||
# (≤ ~4.15) put debs at /v${KVER}/ directly. Try /amd64/ first;
|
||||
# fall back to bare. linux-image-unsigned was renamed from
|
||||
# linux-image- around 4.18 — old kernels use the plain name.
|
||||
BASE="https://kernel.ubuntu.com/mainline/v${KVER}"
|
||||
for URL in "${BASE}/amd64/" "${BASE}/"; do
|
||||
INDEX=$(curl -sL "$URL")
|
||||
if echo "$INDEX" | grep -q '\\.deb"'; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
TMP=$(mktemp -d)
|
||||
cd "$TMP"
|
||||
# Pick the 4 canonical generic-kernel .debs by pattern match against
|
||||
# the directory index. Skip lowlatency variants.
|
||||
DEBS=$(curl -sL "$URL" | \\
|
||||
# the directory index. Skip lowlatency variants. Accept both
|
||||
# 'linux-image-unsigned-' (newer) and 'linux-image-' (older).
|
||||
DEBS=$(echo "$INDEX" | \\
|
||||
grep -oE 'href="[^"]+\\.deb"' | sed 's/href="//; s/"$//' | \\
|
||||
grep -E '(linux-image-unsigned|linux-modules|linux-headers)-[0-9.]+-[0-9]+-generic_|linux-headers-[0-9.]+-[0-9]+_[^_]+_all\\.deb' | \\
|
||||
grep -E '(linux-image(-unsigned)?|linux-modules|linux-headers)-[0-9.]+-[0-9]+-generic_|linux-headers-[0-9.]+-[0-9]+_[^_]+_all\\.deb' | \\
|
||||
grep -v lowlatency)
|
||||
if [ -z "$DEBS" ]; then
|
||||
echo "[-] no .debs found at $URL — does the version exist on kernel.ubuntu.com?" >&2
|
||||
echo "[-] no .debs found at ${BASE}/ (tried /amd64/ and bare)" >&2
|
||||
exit 2
|
||||
fi
|
||||
for f in $DEBS; do
|
||||
@@ -119,12 +150,46 @@ Vagrant.configure("2") do |c|
|
||||
curl -fsSL -O "${URL}${f}"
|
||||
done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
dpkg -i *.deb || apt-get install -f -y -qq
|
||||
# --force-depends so packages still install even when t64-transition
|
||||
# libs (libssl3t64, libelf1t64) are missing on a pre-24.04 rootfs.
|
||||
# The kernel image + modules don't actually need those at boot —
|
||||
# the dependency is for signing/integrity checks at build time.
|
||||
dpkg -i --force-depends *.deb || apt-get install -f -y -qq || true
|
||||
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
|
||||
echo "[i] mainline ${KVER} installed; reboot via 'vagrant reload'"
|
||||
SHELL
|
||||
end
|
||||
|
||||
# 2c. Optional per-module provisioner. If
|
||||
# tools/verify-vm/provisioners/<module>.sh exists, run it as root
|
||||
# before build-and-verify. Used for things only meaningful per-module:
|
||||
# build sudo 1.9.16 from source (sudo_chwoot), drop a polkit allow
|
||||
# rule (udisks_libblockdev), add a sudoers grant (sudo_runas_neg1).
|
||||
skk_mod = ENV["SKK_MODULE"] || ""
|
||||
if !skk_mod.empty?
|
||||
prov_path = File.join(__dir__, "provisioners", "#{skk_mod}.sh")
|
||||
if File.exist?(prov_path)
|
||||
m.vm.provision "shell", name: "module-provision-#{skk_mod}",
|
||||
path: prov_path
|
||||
end
|
||||
end
|
||||
|
||||
# 3. Build SKELETONKEY in-VM and run --explain --active for the target
|
||||
# module. Runs as the unprivileged 'vagrant' user (NOT root) — most
|
||||
# detect()s gate on "are you already root?" and short-circuit if so,
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# CVE-2025-32463 sudo --chroot NSS injection (Stratascale). Vulnerable
|
||||
# range is sudo [1.9.14, 1.9.17p0]. Ubuntu 22.04 ships 1.9.9 which
|
||||
# PREDATES the --chroot code path. Build sudo 1.9.16p1 from upstream
|
||||
# and install to /usr/local (which precedes /usr/bin in Ubuntu's default
|
||||
# PATH so plain `sudo` resolves to the vulnerable binary).
|
||||
set -e
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get install -y -qq libpam0g-dev libssl-dev wget make gcc >/dev/null
|
||||
|
||||
cd /tmp
|
||||
TARBALL=sudo-1.9.16p1.tar.gz
|
||||
URL="https://www.sudo.ws/dist/${TARBALL}"
|
||||
|
||||
if [ -x /usr/local/bin/sudo ] && /usr/local/bin/sudo --version 2>&1 | head -1 | grep -q "1.9.16p1"; then
|
||||
echo "[=] sudo 1.9.16p1 already at /usr/local/bin/sudo"
|
||||
else
|
||||
[ -f "${TARBALL}" ] || wget -q "${URL}"
|
||||
rm -rf sudo-1.9.16p1
|
||||
tar xzf "${TARBALL}"
|
||||
cd sudo-1.9.16p1
|
||||
# --sysconfdir=/etc so it honors the existing /etc/sudoers (vagrant's
|
||||
# NOPASSWD grant). --disable-shared keeps the build self-contained.
|
||||
./configure --prefix=/usr/local --sysconfdir=/etc \
|
||||
--disable-shared --quiet >/dev/null 2>&1
|
||||
make -j"$(nproc)" >/tmp/sudo-build.log 2>&1 || { tail -40 /tmp/sudo-build.log; exit 1; }
|
||||
make install >/tmp/sudo-install.log 2>&1 || { tail -40 /tmp/sudo-install.log; exit 1; }
|
||||
fi
|
||||
|
||||
# Verify what the unprivileged user's PATH resolves to.
|
||||
echo "[+] which sudo (root): $(which sudo)"
|
||||
echo "[+] /usr/local/bin/sudo version: $(/usr/local/bin/sudo --version | head -1)"
|
||||
sudo -u vagrant bash -c 'echo "[+] vagrant PATH: $PATH"; echo "[+] vagrant sees: $(which sudo)"; sudo --version | head -1'
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# CVE-2019-14287 needs a (ALL,!root) grant for find_runas_blacklist_grant()
|
||||
# to fire. Ubuntu 18.04 ships sudo 1.8.21p2 (in the vulnerable range) but
|
||||
# Vagrant's default sudoers doesn't include the grant. Add it.
|
||||
set -e
|
||||
|
||||
cat >/etc/sudoers.d/99-skk-runas-neg1 <<'EOF'
|
||||
vagrant ALL=(ALL,!root) NOPASSWD: /bin/vi
|
||||
EOF
|
||||
chmod 0440 /etc/sudoers.d/99-skk-runas-neg1
|
||||
|
||||
echo "[+] sudoers grant installed:"
|
||||
grep . /etc/sudoers.d/99-skk-runas-neg1
|
||||
echo
|
||||
echo "[+] sudo -ln -U vagrant tail:"
|
||||
sudo -ln -U vagrant 2>&1 | tail -10 || true
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# CVE-2025-6019 udisks/libblockdev SUID-on-mount (Qualys). Debian 12's
|
||||
# cloud image is server-oriented and doesn't ship udisks2. Install it,
|
||||
# and drop a polkit rule allowing the vagrant user to invoke the
|
||||
# affected action.ids — the real-world bug-path is "active console
|
||||
# user invokes loop-setup", and we don't have a graphical session in
|
||||
# Vagrant. The polkit rule simulates the trust polkit would give a
|
||||
# logged-in workstation user.
|
||||
set -e
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get install -y -qq udisks2 libblockdev-utils2 >/dev/null
|
||||
|
||||
mkdir -p /etc/polkit-1/rules.d
|
||||
cat >/etc/polkit-1/rules.d/49-skk-verify.rules <<'EOF'
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (subject.user == "vagrant" &&
|
||||
(action.id == "org.freedesktop.UDisks2.loop-setup" ||
|
||||
action.id == "org.freedesktop.UDisks2.filesystem-mount" ||
|
||||
action.id == "org.freedesktop.UDisks2.filesystem-mount-other-seat" ||
|
||||
action.id == "org.freedesktop.UDisks2.modify-device")) {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
EOF
|
||||
|
||||
systemctl enable udisks2.service >/dev/null 2>&1 || true
|
||||
systemctl restart udisks2.service
|
||||
sleep 2
|
||||
|
||||
echo "[+] udisks2 status:"
|
||||
systemctl is-active udisks2.service
|
||||
echo "[+] udisks2 version: $(dpkg-query -W -f='${Version}' udisks2)"
|
||||
echo "[+] libblockdev version: $(dpkg-query -W -f='${Version}' libblockdev-utils2)"
|
||||
@@ -35,7 +35,7 @@ af_packet:
|
||||
box: ubuntu1804
|
||||
kernel_pkg: "" # stock 4.15.0-213-generic — patch backported
|
||||
kernel_version: "4.15.0"
|
||||
expect_detect: OK
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2017-7308; bug fixed mainline 4.10.6 + 4.9.18 backports. Ubuntu 18.04 stock kernel (4.15.0) is post-fix — detect() correctly returns OK. To validate the VULNERABLE path empirically would need a hand-built 4.4 or earlier kernel; deferred."
|
||||
|
||||
af_packet2:
|
||||
@@ -71,7 +71,7 @@ dirty_cow:
|
||||
box: ubuntu1804
|
||||
kernel_pkg: "" # 4.15.0 has the COW race fix; need older kernel
|
||||
kernel_version: "4.4.0"
|
||||
expect_detect: OK
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2016-5195; ALL 4.4+ kernels have the fix backported. Ubuntu 18.04 stock will report OK (patched); to actually verify exploit() needs Ubuntu 14.04 / kernel ≤ 4.4.0-46. Use a custom box for that."
|
||||
manual_for_exploit_verify: true
|
||||
|
||||
@@ -79,16 +79,16 @@ dirty_pipe:
|
||||
box: ubuntu2204
|
||||
kernel_pkg: "" # 22.04 stock 5.15.0-91-generic
|
||||
kernel_version: "5.15.0"
|
||||
expect_detect: OK
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2022-0847; introduced 5.8, fixed 5.16.11 / 5.15.25. Ubuntu 22.04 ships 5.15.0-91-generic, where uname reports '5.15.0' (below the 5.15.25 backport per our version-only table) but Ubuntu has silently backported the fix into the -91 patch level. Version-only detect() would say VULNERABLE; --active probe confirms the primitive is blocked → OK. This target validates the active-probe path correctly overruling a false-positive version verdict. (Originally pointed at Ubuntu 20.04 + pinned 5.13.0-19, but that HWE kernel is no longer in 20.04's apt archive.)"
|
||||
|
||||
dirtydecrypt:
|
||||
box: debian12
|
||||
kernel_pkg: "" # only Linux 7.0+ has the bug — needs custom kernel
|
||||
kernel_version: "7.0.0"
|
||||
expect_detect: OK
|
||||
notes: "CVE-2026-31635; bug introduced in 7.0 rxgk path. NO mainline 7.0 distro shipping yet — Debian 12 will report OK (predates the bug). Verifying exploit() needs a hand-built 7.0-rc kernel."
|
||||
manual_for_exploit_verify: true
|
||||
box: ubuntu2204
|
||||
kernel_pkg: ""
|
||||
mainline_version: "6.19.7" # below the 6.19.13 backport → genuinely vulnerable
|
||||
kernel_version: "6.19.7"
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2026-31635; rxgk RESPONSE oversized auth_len. Per NVD: bug entered at 6.16.1, vulnerable through 6.18.22 / 6.19.12 / 7.0-rc7; fixed at 6.18.23 / 6.19.13 / 7.0 stable. Mainline 6.19.7 is below the .13 backport → genuinely VULNERABLE. (Earlier module code wrongly gated 'predates' on 7.0; fixed in this commit by gating on 6.16.1 + adding 6.18.23 to the backport table.)"
|
||||
|
||||
entrybleed:
|
||||
box: ubuntu2204
|
||||
@@ -98,12 +98,12 @@ entrybleed:
|
||||
notes: "CVE-2023-0458; side-channel applies to any KPTI-on Intel x86_64 host. Stock Ubuntu 22.04 will report VULNERABLE if meltdown sysfs shows 'Mitigation: PTI'."
|
||||
|
||||
fragnesia:
|
||||
box: debian12
|
||||
box: ""
|
||||
kernel_pkg: ""
|
||||
kernel_version: "7.0.0"
|
||||
expect_detect: OK
|
||||
notes: "CVE-2026-46300; XFRM ESP-in-TCP bug. Needs 7.0-rc; Debian 12 reports OK."
|
||||
manual_for_exploit_verify: true
|
||||
kernel_version: ""
|
||||
expect_detect: ""
|
||||
manual: true
|
||||
notes: "CVE-2026-46300; XFRM ESP-in-TCP bug. Fix lands at 7.0.9. Verifying VULNERABLE needs a pre-fix 7.0.x kernel. Mainline 7.0.5 was tried via Ubuntu 22.04 + kernel.ubuntu.com — fails because the 7.0.5 kernel .debs depend on the t64-transition libs (libssl3t64, libelf1t64) which only exist on Ubuntu 24.04+ / Debian 13+. No Vagrant box with Parallels provider has those libs yet. dpkg --force-depends leaves the kernel image in iHR (broken) state with no /boot/vmlinuz deposited. Resolution: wait for a Parallels-supported ubuntu2404 / debian13 box, or build one locally."
|
||||
|
||||
fuse_legacy:
|
||||
box: debian11
|
||||
@@ -224,42 +224,43 @@ vmwgfx:
|
||||
# ── v0.8.0 additions ──────────────────────────────────────────────
|
||||
|
||||
sudo_chwoot:
|
||||
box: ubuntu2204 # 22.04 ships sudo 1.9.9 (pre-feature) — need a 1.9.14+ install
|
||||
box: ubuntu2204 # 22.04 ships sudo 1.9.9 — provisioner builds 1.9.16p1 over it
|
||||
kernel_pkg: "" # this bug is sudo-version-gated, not kernel
|
||||
kernel_version: "5.15.0"
|
||||
expect_detect: OK
|
||||
notes: "CVE-2025-32463; sudo --chroot NSS shim. Vulnerable range is sudo [1.9.14, 1.9.17p0]. Ubuntu 22.04 ships sudo 1.9.9 which PREDATES the vulnerable --chroot code path — so detect correctly returns OK. To validate VULNERABLE empirically, provision a vulnerable sudo build into the VM (e.g. apt install -t backports sudo=1.9.16-1 or build from source). Deferred."
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2025-32463; sudo --chroot NSS shim. Vulnerable range is sudo [1.9.14, 1.9.17p0]. provisioners/sudo_chwoot.sh builds sudo 1.9.16p1 from upstream sources into /usr/local/bin (which precedes /usr/bin in PATH so plain `sudo` resolves to the vulnerable binary)."
|
||||
|
||||
udisks_libblockdev:
|
||||
box: debian12 # 12 ships udisks2 2.10.x + libblockdev 3.0.x — vulnerable
|
||||
kernel_pkg: ""
|
||||
kernel_version: "6.1.0"
|
||||
expect_detect: PRECOND_FAIL
|
||||
notes: "CVE-2025-6019; udisks/libblockdev SUID-on-mount. Debian 12's cloud image is server-oriented — udisksd is NOT installed by default. detect correctly returns PRECOND_FAIL ('udisksd not installed; bug unreachable here'). To validate VULNERABLE empirically, install udisks2 + log in as an active-session user (Vagrant SSH session is NOT active per polkit — needs a real console session). Both gates are real and the detect honestly surfaces them; deferred."
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2025-6019; udisks/libblockdev SUID-on-mount. provisioners/udisks_libblockdev.sh installs udisks2 + libblockdev-utils3 and drops a polkit rule allowing the vagrant user to invoke loop-setup/filesystem-mount — simulating the trust polkit would give a logged-in workstation user (the real-world bug-path). Without that rule, the SSH session is not 'active' per polkit and the D-Bus call short-circuits."
|
||||
|
||||
pintheft:
|
||||
box: "" # RDS is blacklisted on every common Vagrant box's stock kernel
|
||||
kernel_pkg: ""
|
||||
kernel_version: ""
|
||||
expect_detect: OK
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2026-43494; PinTheft. Among Vagrant-supported distros, NONE autoload the rds kernel module (Arch Linux is the only common distro that does, and there's no maintained generic/arch-linux Vagrant box). On Debian/Ubuntu/Fedora boxes the AF_RDS socket() call fails with EAFNOSUPPORT → detect correctly returns OK ('bug exists in kernel but unreachable from userland here'). Verifying the VULNERABLE path needs either an Arch box, or a custom box with the rds module pre-loaded ('modprobe rds && modprobe rds_tcp'). Deferred."
|
||||
manual: true
|
||||
|
||||
# ── v0.9.0 additions (gap fillers 2018 / 2019 / 2020 / 2024) ──────
|
||||
|
||||
mutagen_astronomy:
|
||||
box: ubuntu1804 # 4.15.0-213 stock — already > 4.14.71 backport → OK
|
||||
box: ""
|
||||
kernel_pkg: ""
|
||||
kernel_version: "4.15.0"
|
||||
expect_detect: OK
|
||||
notes: "CVE-2018-14634; Qualys Mutagen Astronomy. Ubuntu 18.04 ships 4.15.0-213 which is post-fix. detect correctly returns OK. Verifying the VULNERABLE path empirically needs a 2.6.x / 3.10.x EOL kernel (e.g. RHEL 6 / CentOS 6 / Debian 7); deferred to a custom-box workflow."
|
||||
kernel_version: ""
|
||||
expect_detect: ""
|
||||
manual: true
|
||||
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:
|
||||
box: ubuntu1804 # ships sudo 1.8.21p2 (vulnerable; pre-1.8.28 fix)
|
||||
kernel_pkg: ""
|
||||
kernel_version: "4.15.0"
|
||||
expect_detect: PRECOND_FAIL
|
||||
notes: "CVE-2019-14287; sudo Runas -u#-1. Ubuntu 18.04 ships sudo 1.8.21p2 which IS in the vulnerable range — but the default vagrant user has no (ALL,!root) sudoers grant for find_runas_blacklist_grant() to abuse, so detect correctly returns PRECOND_FAIL. To validate VULNERABLE empirically, provision a sudoers entry of the form 'vagrant ALL=(ALL,!root) /bin/vi' before verifying."
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2019-14287; sudo Runas -u#-1. Ubuntu 18.04 ships sudo 1.8.21p2 (vulnerable). provisioners/sudo_runas_neg1.sh adds 'vagrant ALL=(ALL,!root) NOPASSWD: /bin/vi' to /etc/sudoers.d/ so find_runas_blacklist_grant() has a grant to abuse."
|
||||
|
||||
tioscpgrp:
|
||||
box: ubuntu2004 # 5.4 stock kernels (5.4.0-26) are below the 5.4.85 backport
|
||||
@@ -272,13 +273,14 @@ vsock_uaf:
|
||||
box: "" # vsock module typically not loaded on CI containers (no virtualization)
|
||||
kernel_pkg: ""
|
||||
kernel_version: ""
|
||||
expect_detect: OK
|
||||
expect_detect: VULNERABLE
|
||||
notes: "CVE-2024-50264; Pwn2Own 2024 vsock UAF. AF_VSOCK requires the vsock kernel module, which autoloads only on KVM/QEMU GUESTS. Vagrant VMs running under Parallels are themselves guests, but their guest kernel may or may not have vsock loaded depending on the Parallels host. detect correctly returns OK when AF_VSOCK is unavailable. To validate VULNERABLE, ensure the VM kernel has CONFIG_VSOCKETS + virtio-vsock loaded ('modprobe vsock_loopback' may suffice on newer kernels)."
|
||||
manual: true
|
||||
|
||||
nft_pipapo:
|
||||
box: ubuntu2204 # 5.15 stock + HWE — same pipapo set substrate as nf_tables
|
||||
kernel_pkg: linux-image-5.15.0-43-generic
|
||||
kernel_version: "5.15.0-43"
|
||||
kernel_pkg: ""
|
||||
mainline_version: "5.15.5"
|
||||
kernel_version: "5.15.5"
|
||||
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
@@ -139,19 +139,6 @@ if ! vagrant status "$VM_HOSTNAME" 2>&1 | grep -q "running"; then
|
||||
vagrant up "$VM_HOSTNAME" --provider=parallels
|
||||
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"
|
||||
|
||||
# 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..."
|
||||
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..."
|
||||
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
|
||||
# (e.g. " skk-pwnkit: VERDICT: VULNERABLE"), so anchor on the VERDICT
|
||||
|
||||
Reference in New Issue
Block a user