58fb2e0951
nav: removed Releases / CVEs / Defenders links — kept only a
right-aligned GitHub link with the Octocat SVG icon.
index.html: replaced pill-grid corpus view with a proper sortable
table — Year, CVE, Bug, Module, Tier columns. Click headers to
sort. Defaults to Year descending. 28 rows covering 2016 → 2026.
style.css: added .nav-github (border-pill style) + table styles
(sortable headers with arrow indicators, hover rows, mobile-
responsive font-size + overflow-x scroll).
JS for sort is ~25 lines vanilla — no library.
329 lines
18 KiB
HTML
329 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>SKELETONKEY — Curated Linux LPE corpus with detection rules</title>
|
|
<meta name="description" content="One curated binary. 28 Linux privilege-escalation exploits from 2016 → 2026. Auditd + sigma + yara + falco rules in the box. One command picks the safest LPE and runs it.">
|
|
<meta property="og:title" content="SKELETONKEY — Curated Linux LPE corpus">
|
|
<meta property="og:description" content="28 Linux LPE exploits, 2016 → 2026, with detection rules in the box. One command picks the safest one and runs it.">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:url" content="https://karazajac.github.io/SKELETONKEY/">
|
|
<meta name="twitter:card" content="summary">
|
|
<link rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="nav">
|
|
<span class="nav-brand">SKELETONKEY</span>
|
|
<a class="nav-github" href="https://github.com/KaraZajac/SKELETONKEY"
|
|
aria-label="View on GitHub">
|
|
<svg height="20" viewBox="0 0 16 16" width="20" fill="currentColor" aria-hidden="true">
|
|
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38
|
|
0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13
|
|
-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66
|
|
.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15
|
|
-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0
|
|
1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82
|
|
1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01
|
|
1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
|
|
</svg>
|
|
<span>GitHub</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<header class="hero">
|
|
<div class="container">
|
|
<h1>SKELETONKEY</h1>
|
|
<p class="tag">
|
|
One curated binary. <strong>28 Linux LPE exploits</strong> from
|
|
2016 → 2026. Detection rules in the box.
|
|
<strong>One command picks the safest one and runs it.</strong>
|
|
</p>
|
|
|
|
<div class="install-block">
|
|
<button class="copy" onclick="copyInstall(this)">copy</button>
|
|
<pre id="install-cmd"><span class="prompt">$</span> curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \
|
|
&& skeletonkey --auto --i-know</pre>
|
|
</div>
|
|
|
|
<p class="warn">⚠ Authorized testing only — see <a href="https://github.com/KaraZajac/SKELETONKEY/blob/main/docs/ETHICS.md">ETHICS.md</a></p>
|
|
|
|
<div class="cta-row">
|
|
<a class="btn btn-primary" href="https://github.com/KaraZajac/SKELETONKEY/releases/latest">Latest release</a>
|
|
<a class="btn" href="https://github.com/KaraZajac/SKELETONKEY">View on GitHub</a>
|
|
<a class="btn" href="https://github.com/KaraZajac/SKELETONKEY/blob/main/CVES.md">Full CVE inventory</a>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>Why this exists</h2>
|
|
<p class="lead">
|
|
Most Linux privesc tooling is broken in one of three ways:
|
|
</p>
|
|
<ul class="tight">
|
|
<li><strong>linux-exploit-suggester / linpeas</strong> — tell you what <em>might</em> work, run nothing</li>
|
|
<li><strong>auto-root-exploit / kernelpop</strong> — bundle exploits but ship no detection signatures and went stale years ago</li>
|
|
<li><strong>Per-CVE PoC repos</strong> — one author, one distro, abandoned within months</li>
|
|
</ul>
|
|
<p class="lead" style="margin-top:1rem">
|
|
SKELETONKEY is one binary, actively maintained, with detection
|
|
rules for every CVE it bundles — same project for red and blue
|
|
teams.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>Corpus at a glance</h2>
|
|
|
|
<div class="stats">
|
|
<div class="stat">
|
|
<span class="stat-num">28</span>
|
|
<span class="stat-label">total modules</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-num green">14</span>
|
|
<span class="stat-label">🟢 land root by default</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-num yellow">14</span>
|
|
<span class="stat-label">🟡 primitive + opt-in chain</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-num">10y</span>
|
|
<span class="stat-label">2016 → 2026 coverage</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p style="color: var(--text-muted); font-size:0.92rem; margin:0.5rem 0 1rem;">
|
|
Sortable by clicking column headers. 🟢 = lands root by
|
|
default · 🟡 = primitive + opt-in <code>--full-chain</code>.
|
|
</p>
|
|
|
|
<div class="table-wrap">
|
|
<table class="cve-table" id="cve-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-key="year" class="sortable" data-dir="desc">Year</th>
|
|
<th data-key="cve" class="sortable">CVE</th>
|
|
<th data-key="bug">Bug</th>
|
|
<th data-key="module" class="sortable">Module</th>
|
|
<th data-key="tier" class="sortable">Tier</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>2024</td><td>CVE-2024-1086</td><td>nf_tables <code>nft_verdict_init</code> cross-cache UAF</td><td><code>nf_tables</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-32233</td><td>nf_tables anonymous-set UAF</td><td><code>nft_set_uaf</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-22809</td><td>sudoedit <code>EDITOR</code>/<code>VISUAL</code> <code>--</code> argv escape</td><td><code>sudoedit_editor</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-4622</td><td>AF_UNIX garbage-collector race UAF</td><td><code>af_unix_gc</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-3269</td><td>StackRot — maple-tree VMA-split UAF</td><td><code>stackrot</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-2008</td><td>vmwgfx DRM buffer-object OOB write</td><td><code>vmwgfx</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-0386</td><td>overlayfs <code>copy_up</code> preserves setuid bit</td><td><code>overlayfs_setuid</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-0458</td><td>EntryBleed — KPTI prefetchnta KASLR bypass</td><td><code>entrybleed</code></td><td><span class="tier green">🟢 leak</span></td></tr>
|
|
<tr><td>2023</td><td>CVE-2023-0179</td><td>nft_payload set-id memory corruption</td><td><code>nft_payload</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2022</td><td>CVE-2022-25636</td><td>nft_fwd_dup_netdev_offload heap OOB</td><td><code>nft_fwd_dup</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2022</td><td>CVE-2022-2588</td><td>net/sched cls_route4 dangling-filter UAF</td><td><code>cls_route4</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2022</td><td>CVE-2022-0492</td><td>cgroup v1 <code>release_agent</code> ns mismatch</td><td><code>cgroup_release_agent</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2022</td><td>CVE-2022-0847</td><td>Dirty Pipe — page-cache write via splice</td><td><code>dirty_pipe</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2022</td><td>CVE-2022-0185</td><td>fsconfig <code>legacy_parse_param</code> 4k heap OOB</td><td><code>fuse_legacy</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2021</td><td>CVE-2021-33909</td><td>Sequoia — <code>seq_file</code> size_t→int wrap</td><td><code>sequoia</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2021</td><td>CVE-2021-3156</td><td>sudo Baron Samedit heap overflow</td><td><code>sudo_samedit</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2021</td><td>CVE-2021-3493</td><td>Ubuntu overlayfs userns file-cap injection</td><td><code>overlayfs</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2021</td><td>CVE-2021-22555</td><td>iptables xt_compat 4-byte heap OOB</td><td><code>netfilter_xtcompat</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2021</td><td>CVE-2021-4034</td><td>Pwnkit — pkexec NULL argv env-injection</td><td><code>pwnkit</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2020</td><td>CVE-2020-14386</td><td>AF_PACKET <code>tp_reserve</code> integer underflow</td><td><code>af_packet2</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2019</td><td>CVE-2019-13272</td><td>PTRACE_TRACEME → setuid execve race</td><td><code>ptrace_traceme</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2017</td><td>CVE-2017-7308</td><td>AF_PACKET TPACKET_V3 integer overflow</td><td><code>af_packet</code></td><td><span class="tier yellow">🟡 primitive</span></td></tr>
|
|
<tr><td>2016</td><td>CVE-2016-5195</td><td>Dirty COW — COW race via <code>/proc/self/mem</code></td><td><code>dirty_cow</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2026</td><td>CVE-2026-31431</td><td>Copy Fail — algif_aead authencesn page-cache write</td><td><code>copy_fail</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2026</td><td>CVE-2026-43284</td><td>Dirty Frag — IPv4 xfrm-ESP page-cache write</td><td><code>dirty_frag_esp</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2026</td><td>CVE-2026-43284</td><td>Dirty Frag — IPv6 xfrm-ESP (esp6)</td><td><code>dirty_frag_esp6</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2026</td><td>CVE-2026-43500</td><td>Dirty Frag — RxRPC handshake forgery</td><td><code>dirty_frag_rxrpc</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
<tr><td>2026</td><td>variant</td><td>Copy Fail GCM — rfc4106(gcm(aes)) sibling</td><td><code>copy_fail_gcm</code></td><td><span class="tier green">🟢 full chain</span></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>Who it's for</h2>
|
|
<div class="cards">
|
|
<div class="card">
|
|
<h3>🔴 Red team / pentesters</h3>
|
|
<p>One tested binary. <code>--auto</code> ranks vulnerable modules by safety and runs the safest. Honest scope reporting — never claims root it didn't actually get. No more curating stale PoC repos.</p>
|
|
</div>
|
|
<div class="card">
|
|
<h3>🔵 Blue team / SOC</h3>
|
|
<p>Auditd + sigma + yara + falco rules for every CVE. One command ships SIEM coverage: <code>--detect-rules --format=auditd | sudo tee /etc/audit/rules.d/99-skeletonkey.rules</code>.</p>
|
|
</div>
|
|
<div class="card">
|
|
<h3>🛠 Sysadmins</h3>
|
|
<p><code>skeletonkey --scan</code> (no sudo needed) tells you which boxes still need patching. JSON output for CI gates. Fleet-scan tool included. No SaaS, no telemetry.</p>
|
|
</div>
|
|
<div class="card">
|
|
<h3>🎓 CTF / training</h3>
|
|
<p>Reproducible LPE environment with public CVEs across a 10-year timeline. Each module documents the bug, the trigger, and the fix. Detection rules let you practice both sides.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>What it looks like</h2>
|
|
<p class="lead"><code>--auto</code> on a vulnerable Ubuntu 22.04 box:</p>
|
|
|
|
<pre class="code"><span class="prompt">$</span> id
|
|
uid=1000(kara) gid=1000(kara) groups=1000(kara)
|
|
|
|
<span class="prompt">$</span> skeletonkey --auto --i-know
|
|
<span class="hl-muted">[*]</span> auto: host=demo kernel=5.15.0-56-generic arch=x86_64
|
|
<span class="hl-muted">[*]</span> auto: scanning 28 modules for vulnerabilities...
|
|
<span class="hl-green">[+]</span> auto: dirty_pipe <span class="hl-yellow">VULNERABLE</span> (safety rank 90)
|
|
<span class="hl-green">[+]</span> auto: cgroup_release_agent <span class="hl-yellow">VULNERABLE</span> (safety rank 98)
|
|
<span class="hl-green">[+]</span> auto: pwnkit <span class="hl-yellow">VULNERABLE</span> (safety rank 100)
|
|
|
|
<span class="hl-muted">[*]</span> auto: 3 vulnerable modules found. Safest is <span class="hl-accent">'pwnkit'</span> (rank 100).
|
|
<span class="hl-muted">[*]</span> auto: launching --exploit pwnkit...
|
|
|
|
<span class="hl-green">[+]</span> pwnkit: writing gconv-modules cache + payload.so...
|
|
<span class="hl-green">[+]</span> pwnkit: execve(pkexec) with NULL argv + crafted envp...
|
|
<span class="hl-green">#</span> id
|
|
uid=0(root) gid=0(root) groups=0(root)</pre>
|
|
|
|
<p style="color: var(--text-muted); font-size: 0.92rem; margin-top: 1rem">
|
|
Safety ranking goes <strong>structural escapes</strong> →
|
|
<strong>page-cache writes</strong> →
|
|
<strong>userspace cred-races</strong> →
|
|
<strong>kernel primitives</strong> →
|
|
<strong>kernel races</strong>. The goal is to never crash a
|
|
production box looking for root.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>The verified-vs-claimed bar</h2>
|
|
<p class="lead">
|
|
Most public PoC repos hardcode offsets for one kernel build and
|
|
silently break elsewhere. SKELETONKEY refuses to ship fabricated
|
|
offsets.
|
|
</p>
|
|
<ul class="tight">
|
|
<li>The shared <code>--full-chain</code> finisher returns <code>EXPLOIT_OK</code> only when a setuid bash sentinel file <em>actually appears</em></li>
|
|
<li>Modules with a primitive but no portable cred-overwrite chain default to firing the primitive + grooming the slab + recording a witness, then return <code>EXPLOIT_FAIL</code> with diagnostic</li>
|
|
<li>Operators populate the offset table once per kernel via <code>skeletonkey --dump-offsets</code> (parses <code>/proc/kallsyms</code> or <code>/boot/System.map</code>) and upstream the entry via PR — see <a href="https://github.com/KaraZajac/SKELETONKEY/blob/main/CONTRIBUTING.md">CONTRIBUTING.md</a></li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>Quickstart commands</h2>
|
|
|
|
<pre class="code"><span class="cmt"># Install (x86_64 / arm64; checksum-verified)</span>
|
|
<span class="prompt">$</span> curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh
|
|
|
|
<span class="cmt"># What's this box vulnerable to? (no sudo)</span>
|
|
<span class="prompt">$</span> skeletonkey --scan
|
|
|
|
<span class="cmt"># Pick the safest LPE and run it</span>
|
|
<span class="prompt">$</span> skeletonkey --auto --i-know
|
|
|
|
<span class="cmt"># Deploy detection rules (needs sudo to write into /etc/audit/rules.d/)</span>
|
|
<span class="prompt">$</span> skeletonkey --detect-rules --format=auditd \
|
|
| sudo tee /etc/audit/rules.d/99-skeletonkey.rules
|
|
|
|
<span class="cmt"># Fleet scan — many hosts via SSH, aggregated JSON for SIEM</span>
|
|
<span class="prompt">$</span> ./tools/skeletonkey-fleet-scan.sh --binary skeletonkey \
|
|
--ssh-key ~/.ssh/id_rsa hosts.txt</pre>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<div class="container">
|
|
<h2>Status</h2>
|
|
<p class="lead">
|
|
<strong>v0.5.0</strong> cut 2026-05-17. 28 modules build clean
|
|
on Debian 13 (kernel 6.12) and refuse cleanly on patched hosts.
|
|
Empirical end-to-end validation on a vulnerable-kernel VM matrix
|
|
is the next roadmap item; until then, the corpus is best
|
|
understood as "compiles + detects + structurally correct +
|
|
honest on failure."
|
|
</p>
|
|
<p style="margin-top:1rem">
|
|
<a class="btn" href="https://github.com/KaraZajac/SKELETONKEY/blob/main/ROADMAP.md">Read the roadmap</a>
|
|
<a class="btn" href="https://github.com/KaraZajac/SKELETONKEY/blob/main/CONTRIBUTING.md">How to contribute</a>
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<footer>
|
|
<div class="container">
|
|
<p>
|
|
Each module credits the original CVE reporter and PoC author in its
|
|
<code>NOTICE.md</code>. The research credit belongs to the people
|
|
who found the bugs.
|
|
</p>
|
|
<p>
|
|
MIT licensed ·
|
|
<a href="https://github.com/KaraZajac/SKELETONKEY">github.com/KaraZajac/SKELETONKEY</a>
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
function copyInstall(btn) {
|
|
var cmd = document.getElementById('install-cmd').innerText.replace(/^\$\s*/, '');
|
|
navigator.clipboard.writeText(cmd).then(function() {
|
|
btn.textContent = 'copied!';
|
|
btn.classList.add('copied');
|
|
setTimeout(function() {
|
|
btn.textContent = 'copy';
|
|
btn.classList.remove('copied');
|
|
}, 1500);
|
|
});
|
|
}
|
|
|
|
/* CVE table sort */
|
|
(function() {
|
|
var table = document.getElementById('cve-table');
|
|
if (!table) return;
|
|
var headers = table.querySelectorAll('th.sortable');
|
|
headers.forEach(function(th, idx) {
|
|
th.style.cursor = 'pointer';
|
|
th.addEventListener('click', function() {
|
|
var tbody = table.querySelector('tbody');
|
|
var rows = Array.prototype.slice.call(tbody.querySelectorAll('tr'));
|
|
var dir = th.getAttribute('data-dir') === 'asc' ? 'desc' : 'asc';
|
|
headers.forEach(function(h) { h.removeAttribute('data-dir'); });
|
|
th.setAttribute('data-dir', dir);
|
|
rows.sort(function(a, b) {
|
|
var av = a.children[idx].innerText.trim();
|
|
var bv = b.children[idx].innerText.trim();
|
|
var na = parseFloat(av), nb = parseFloat(bv);
|
|
if (!isNaN(na) && !isNaN(nb)) { av = na; bv = nb; }
|
|
if (av < bv) return dir === 'asc' ? -1 : 1;
|
|
if (av > bv) return dir === 'asc' ? 1 : -1;
|
|
return 0;
|
|
});
|
|
rows.forEach(function(r) { tbody.appendChild(r); });
|
|
});
|
|
});
|
|
/* default sort: Year desc */
|
|
var first = table.querySelector('th[data-key="year"]');
|
|
if (first) first.click(); /* asc */
|
|
if (first) first.click(); /* desc */
|
|
})();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|