rename: IAMROOT → SKELETONKEY across the entire project
Breaking change. Tool name, binary name, function/type names,
constant names, env vars, header guards, file paths, and GitHub
repo URL all rebrand IAMROOT → SKELETONKEY.
Changes:
- All "IAMROOT" → "SKELETONKEY" (constants, env vars, enum
values, docs, comments)
- All "iamroot" → "skeletonkey" (functions, types, paths, CLI)
- iamroot.c → skeletonkey.c
- modules/*/iamroot_modules.{c,h} → modules/*/skeletonkey_modules.{c,h}
- tools/iamroot-fleet-scan.sh → tools/skeletonkey-fleet-scan.sh
- Binary "iamroot" → "skeletonkey"
- GitHub URL KaraZajac/IAMROOT → KaraZajac/SKELETONKEY
- .gitignore now expects build output named "skeletonkey"
- /tmp/iamroot-* tmpfiles → /tmp/skeletonkey-*
- Env vars IAMROOT_MODPROBE_PATH etc. → SKELETONKEY_*
New ASCII skeleton-key banner (horizontal key icon + ANSI Shadow
SKELETONKEY block letters) replaces the IAMROOT banner in
skeletonkey.c and README.md.
VERSION: 0.3.1 → 0.4.0 (breaking).
Build clean on Debian 6.12.86. `skeletonkey --version` → 0.4.0.
All 24 modules still register; no functional code changes — pure
rename + banner refresh.
This commit is contained in:
+11
-11
@@ -14,7 +14,7 @@ modules/<module_name>/
|
||||
├── MODULE.md # Human-readable writeup of the bug
|
||||
├── NOTICE.md # Credits to original researcher
|
||||
├── kernel-range.json # Machine-readable affected kernels
|
||||
├── module.c # Implements iamroot_module interface
|
||||
├── module.c # Implements skeletonkey_module interface
|
||||
├── module.h
|
||||
├── detect/
|
||||
│ ├── auditd.rules # blue team detection
|
||||
@@ -24,10 +24,10 @@ modules/<module_name>/
|
||||
└── tests/ # per-module tests (run in CI matrix)
|
||||
```
|
||||
|
||||
### `iamroot_module` interface (planned, Phase 1)
|
||||
### `skeletonkey_module` interface (planned, Phase 1)
|
||||
|
||||
```c
|
||||
struct iamroot_module {
|
||||
struct skeletonkey_module {
|
||||
const char *name; /* "copy_fail" */
|
||||
const char *cve; /* "CVE-2026-31431" */
|
||||
const char *summary; /* one-line description */
|
||||
@@ -35,29 +35,29 @@ struct iamroot_module {
|
||||
/* Return 1 if host appears vulnerable, 0 if patched/immune,
|
||||
* -1 if probe couldn't run. May call entrybleed_leak_kbase()
|
||||
* etc. from core/ if a leak primitive is needed. */
|
||||
int (*detect)(struct iamroot_host *host);
|
||||
int (*detect)(struct skeletonkey_host *host);
|
||||
|
||||
/* Run the exploit. Caller has already passed the
|
||||
* authorization gate. Returns 0 on root acquired,
|
||||
* nonzero on failure. */
|
||||
int (*exploit)(struct iamroot_host *host, struct iamroot_opts *opts);
|
||||
int (*exploit)(struct skeletonkey_host *host, struct skeletonkey_opts *opts);
|
||||
|
||||
/* Apply a runtime mitigation for this CVE (sysctl, module
|
||||
* blacklist, etc.). Returns 0 on success. NULL if no
|
||||
* mitigation is offered. */
|
||||
int (*mitigate)(struct iamroot_host *host);
|
||||
int (*mitigate)(struct skeletonkey_host *host);
|
||||
|
||||
/* Undo --exploit-backdoor or --mitigate side effects. */
|
||||
int (*cleanup)(struct iamroot_host *host);
|
||||
int (*cleanup)(struct skeletonkey_host *host);
|
||||
|
||||
/* Affected kernel version range, distros covered, etc. */
|
||||
const struct iamroot_kernel_range *ranges;
|
||||
const struct skeletonkey_kernel_range *ranges;
|
||||
size_t n_ranges;
|
||||
};
|
||||
```
|
||||
|
||||
Modules register themselves at link time via a constructor-attribute
|
||||
table. The top-level `iamroot` binary iterates the registry on each
|
||||
table. The top-level `skeletonkey` binary iterates the registry on each
|
||||
invocation.
|
||||
|
||||
## Shared `core/`
|
||||
@@ -78,7 +78,7 @@ Code that more than one module needs lives in `core/`:
|
||||
|
||||
## Top-level dispatcher
|
||||
|
||||
`iamroot.c` (planned, Phase 1) is the CLI entry point. Responsibilities:
|
||||
`skeletonkey.c` (planned, Phase 1) is the CLI entry point. Responsibilities:
|
||||
|
||||
1. Parse args (`--scan`, `--exploit <name>`, `--mitigate`,
|
||||
`--detect-rules`, `--cleanup`, etc.)
|
||||
@@ -109,7 +109,7 @@ the module).
|
||||
1. `git checkout -b add-cve-XXXX-NNNN`
|
||||
2. `cp -r modules/_stubs/_template modules/<module_name>`
|
||||
3. Fill in `MODULE.md`, `NOTICE.md`, `kernel-range.json`
|
||||
4. Implement `module.c` exposing the `iamroot_module` interface
|
||||
4. Implement `module.c` exposing the `skeletonkey_module` interface
|
||||
5. Ship at least one detection rule under `detect/`
|
||||
6. Add tests under `tests/`
|
||||
7. PR. CI runs the matrix. If it lands root on at least one
|
||||
|
||||
+34
-34
@@ -1,25 +1,25 @@
|
||||
# IAMROOT for defenders
|
||||
# SKELETONKEY for defenders
|
||||
|
||||
IAMROOT is dual-use: the same binary that runs exploits also ships the
|
||||
SKELETONKEY is dual-use: the same binary that runs exploits also ships the
|
||||
detection rules to spot them. This document is for the blue team.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
# 1. Detect what you're vulnerable to (no system modification)
|
||||
sudo iamroot --scan --json | jq .
|
||||
sudo skeletonkey --scan --json | jq .
|
||||
|
||||
# 2. Deploy detection rules covering every bundled CVE
|
||||
sudo iamroot --detect-rules --format=auditd | sudo tee /etc/audit/rules.d/99-iamroot.rules
|
||||
sudo skeletonkey --detect-rules --format=auditd | sudo tee /etc/audit/rules.d/99-skeletonkey.rules
|
||||
sudo systemctl restart auditd
|
||||
|
||||
# 3. (Optional) Apply pre-patch mitigations for vulnerable families
|
||||
sudo iamroot --mitigate copy_fail # or whatever module reports VULNERABLE
|
||||
sudo skeletonkey --mitigate copy_fail # or whatever module reports VULNERABLE
|
||||
|
||||
# 4. Watch
|
||||
sudo ausearch -k iamroot-copy-fail -ts recent
|
||||
sudo ausearch -k iamroot-dirty-pipe -ts recent
|
||||
sudo ausearch -k iamroot-pwnkit -ts recent
|
||||
sudo ausearch -k skeletonkey-copy-fail -ts recent
|
||||
sudo ausearch -k skeletonkey-dirty-pipe -ts recent
|
||||
sudo ausearch -k skeletonkey-pwnkit -ts recent
|
||||
```
|
||||
|
||||
## Why a single tool for offense and defense
|
||||
@@ -27,7 +27,7 @@ sudo ausearch -k iamroot-pwnkit -ts recent
|
||||
Public LPE PoCs ship without detection rules. Public detection rules
|
||||
ship without test corpora. The gap means defenders deploy rules they
|
||||
never validate against a real exploit, and attackers iterate against
|
||||
defenders who haven't tuned thresholds. IAMROOT closes that loop:
|
||||
defenders who haven't tuned thresholds. SKELETONKEY closes that loop:
|
||||
|
||||
- Each module ships an exploit AND the detection rules that catch it.
|
||||
- Every CVE in `CVES.md` has a row in the rule corpus.
|
||||
@@ -41,7 +41,7 @@ defenders who haven't tuned thresholds. IAMROOT closes that loop:
|
||||
### Inventory what's bundled
|
||||
|
||||
```bash
|
||||
iamroot --list
|
||||
skeletonkey --list
|
||||
```
|
||||
|
||||
Prints every registered module with CVE, family, and one-line summary.
|
||||
@@ -49,9 +49,9 @@ Prints every registered module with CVE, family, and one-line summary.
|
||||
### Run all detectors
|
||||
|
||||
```bash
|
||||
iamroot --scan # human-readable
|
||||
iamroot --scan --json # one JSON object → SIEM ingest
|
||||
iamroot --scan --json | jq '.modules[] | select(.result == "VULNERABLE")'
|
||||
skeletonkey --scan # human-readable
|
||||
skeletonkey --scan --json # one JSON object → SIEM ingest
|
||||
skeletonkey --scan --json | jq '.modules[] | select(.result == "VULNERABLE")'
|
||||
```
|
||||
|
||||
Result codes per module:
|
||||
@@ -63,23 +63,23 @@ Result codes per module:
|
||||
| `PRECOND_FAIL` | Preconditions missing (module/feature not installed) | 4 |
|
||||
| `TEST_ERROR` | Probe could not run (permissions, missing tools, etc.) | 1 |
|
||||
|
||||
`iamroot --scan` returns the WORST result code across all modules.
|
||||
`skeletonkey --scan` returns the WORST result code across all modules.
|
||||
Use this in CI to fail builds that produce vulnerable images.
|
||||
|
||||
### Deploy detection rules
|
||||
|
||||
```bash
|
||||
# auditd (most environments)
|
||||
sudo iamroot --detect-rules --format=auditd \
|
||||
| sudo tee /etc/audit/rules.d/99-iamroot.rules
|
||||
sudo skeletonkey --detect-rules --format=auditd \
|
||||
| sudo tee /etc/audit/rules.d/99-skeletonkey.rules
|
||||
sudo augenrules --load # or systemctl restart auditd
|
||||
|
||||
# Sigma (for SIEMs that ingest sigma)
|
||||
iamroot --detect-rules --format=sigma > /etc/falco/iamroot.sigma.yml
|
||||
skeletonkey --detect-rules --format=sigma > /etc/falco/skeletonkey.sigma.yml
|
||||
|
||||
# YARA / Falco — placeholders for future modules; currently empty
|
||||
iamroot --detect-rules --format=yara
|
||||
iamroot --detect-rules --format=falco
|
||||
skeletonkey --detect-rules --format=yara
|
||||
skeletonkey --detect-rules --format=falco
|
||||
```
|
||||
|
||||
Rules are emitted in registry order, deduplicated by string-pointer:
|
||||
@@ -91,19 +91,19 @@ auditd config).
|
||||
|
||||
| Key | Modules | What it catches |
|
||||
|---|---|---|
|
||||
| `iamroot-copy-fail` | copy_fail, copy_fail_gcm, dirty_frag_esp{,6}, dirty_frag_rxrpc | Writes to passwd/shadow/sudoers/su |
|
||||
| `iamroot-copy-fail-afalg` | copy_fail family | AF_ALG socket creation (kernel crypto API used by exploit) |
|
||||
| `iamroot-copy-fail-xfrm` | copy_fail family | xfrm setsockopt (Dirty Frag ESP variants) |
|
||||
| `iamroot-dirty-pipe` | dirty_pipe | Same target files; complements copy-fail watches |
|
||||
| `iamroot-dirty-pipe-splice` | dirty_pipe | splice() syscalls (the bug's primitive) |
|
||||
| `iamroot-pwnkit` | pwnkit | pkexec watch |
|
||||
| `iamroot-pwnkit-execve` | pwnkit | execve of pkexec — combine with audit of argv to catch argc=0 |
|
||||
| `skeletonkey-copy-fail` | copy_fail, copy_fail_gcm, dirty_frag_esp{,6}, dirty_frag_rxrpc | Writes to passwd/shadow/sudoers/su |
|
||||
| `skeletonkey-copy-fail-afalg` | copy_fail family | AF_ALG socket creation (kernel crypto API used by exploit) |
|
||||
| `skeletonkey-copy-fail-xfrm` | copy_fail family | xfrm setsockopt (Dirty Frag ESP variants) |
|
||||
| `skeletonkey-dirty-pipe` | dirty_pipe | Same target files; complements copy-fail watches |
|
||||
| `skeletonkey-dirty-pipe-splice` | dirty_pipe | splice() syscalls (the bug's primitive) |
|
||||
| `skeletonkey-pwnkit` | pwnkit | pkexec watch |
|
||||
| `skeletonkey-pwnkit-execve` | pwnkit | execve of pkexec — combine with audit of argv to catch argc=0 |
|
||||
|
||||
Search:
|
||||
|
||||
```bash
|
||||
sudo ausearch -k iamroot-copy-fail -ts today
|
||||
sudo ausearch -k iamroot-pwnkit -ts today
|
||||
sudo ausearch -k skeletonkey-copy-fail -ts today
|
||||
sudo ausearch -k skeletonkey-pwnkit -ts today
|
||||
```
|
||||
|
||||
### Mitigate (pre-patch)
|
||||
@@ -114,10 +114,10 @@ distro-portable workarounds:
|
||||
```bash
|
||||
# Currently: copy_fail_family — blacklists algif_aead/esp4/esp6/rxrpc,
|
||||
# sets kernel.apparmor_restrict_unprivileged_userns=1, drops caches.
|
||||
sudo iamroot --mitigate copy_fail
|
||||
sudo skeletonkey --mitigate copy_fail
|
||||
|
||||
# Revert mitigation (e.g., before applying the real kernel patch)
|
||||
sudo iamroot --cleanup copy_fail
|
||||
sudo skeletonkey --cleanup copy_fail
|
||||
```
|
||||
|
||||
Modules without `--mitigate` (dirty_pipe, entrybleed, pwnkit) report
|
||||
@@ -131,7 +131,7 @@ The `--scan --json` output is one-line-per-host friendly:
|
||||
```bash
|
||||
# scan a host list via ssh
|
||||
for h in $(cat fleet.txt); do
|
||||
ssh $h sudo iamroot --scan --json | jq --arg h "$h" '. + {host: $h}'
|
||||
ssh $h sudo skeletonkey --scan --json | jq --arg h "$h" '. + {host: $h}'
|
||||
done | jq -s . > fleet-scan-$(date +%F).json
|
||||
|
||||
# group by vulnerability
|
||||
@@ -148,9 +148,9 @@ modification.
|
||||
|
||||
| Rule | False-positive shape |
|
||||
|---|---|
|
||||
| `iamroot-copy-fail-afalg` | strongSwan and IPsec daemons use AF_ALG legitimately — scope with `-F auid=` to exclude service accounts |
|
||||
| `iamroot-dirty-pipe-splice` | nginx, HAProxy, kTLS use splice() heavily — scope with `-F gid!=33 -F gid!=99` for those service accounts |
|
||||
| `iamroot-pwnkit-execve` | gnome-software, polkit's own dispatcher legitimately exec pkexec — scope by parent process if you can correlate |
|
||||
| `skeletonkey-copy-fail-afalg` | strongSwan and IPsec daemons use AF_ALG legitimately — scope with `-F auid=` to exclude service accounts |
|
||||
| `skeletonkey-dirty-pipe-splice` | nginx, HAProxy, kTLS use splice() heavily — scope with `-F gid!=33 -F gid!=99` for those service accounts |
|
||||
| `skeletonkey-pwnkit-execve` | gnome-software, polkit's own dispatcher legitimately exec pkexec — scope by parent process if you can correlate |
|
||||
|
||||
The shipped rules are starting points. Tune per environment.
|
||||
|
||||
|
||||
+53
-53
@@ -1,6 +1,6 @@
|
||||
# IAMROOT detection playbook
|
||||
# SKELETONKEY detection playbook
|
||||
|
||||
Operational guide for blue teams using IAMROOT defensively. Pairs
|
||||
Operational guide for blue teams using SKELETONKEY defensively. Pairs
|
||||
with `docs/DEFENDERS.md` (the "what" reference) — this is the "how to
|
||||
make it part of your daily ops" guide.
|
||||
|
||||
@@ -8,15 +8,15 @@ make it part of your daily ops" guide.
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ inventory │ ← iamroot --list (what's bundled?)
|
||||
│ inventory │ ← skeletonkey --list (what's bundled?)
|
||||
└──────┬──────┘
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ scan │ ← iamroot --scan --json (what am I vulnerable to?)
|
||||
│ scan │ ← skeletonkey --scan --json (what am I vulnerable to?)
|
||||
└──────┬──────┘
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ fleet scan │ ← iamroot-fleet-scan.sh hosts.txt
|
||||
│ fleet scan │ ← skeletonkey-fleet-scan.sh hosts.txt
|
||||
└──────┬──────┘
|
||||
▼
|
||||
┌────────────┼────────────┐
|
||||
@@ -29,7 +29,7 @@ make it part of your daily ops" guide.
|
||||
└────────────┼────────────┘
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ monitor │ ← ausearch -k iamroot-* / SIEM alerts
|
||||
│ monitor │ ← ausearch -k skeletonkey-* / SIEM alerts
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
@@ -39,17 +39,17 @@ make it part of your daily ops" guide.
|
||||
|
||||
```bash
|
||||
# Daily/weekly hygiene check
|
||||
sudo iamroot --scan
|
||||
sudo skeletonkey --scan
|
||||
|
||||
# If anything's VULNERABLE, deploy detections + apply mitigation
|
||||
sudo iamroot --detect-rules --format=auditd | sudo tee /etc/audit/rules.d/99-iamroot.rules
|
||||
sudo skeletonkey --detect-rules --format=auditd | sudo tee /etc/audit/rules.d/99-skeletonkey.rules
|
||||
sudo augenrules --load
|
||||
sudo iamroot --mitigate copy_fail # or whichever module fired
|
||||
sudo skeletonkey --mitigate copy_fail # or whichever module fired
|
||||
```
|
||||
|
||||
### Small fleet (~10-100 hosts, SSH-reachable)
|
||||
|
||||
Use `tools/iamroot-fleet-scan.sh`:
|
||||
Use `tools/skeletonkey-fleet-scan.sh`:
|
||||
|
||||
```bash
|
||||
# Hosts list — one per line; user@host:port supported
|
||||
@@ -61,8 +61,8 @@ ops@db-01:2222
|
||||
EOF
|
||||
|
||||
# Scan; binary scp'd, run, cleaned up. Output is one JSON doc.
|
||||
./iamroot-fleet-scan.sh \
|
||||
--binary ./iamroot \
|
||||
./skeletonkey-fleet-scan.sh \
|
||||
--binary ./skeletonkey \
|
||||
--ssh-key ~/.ssh/ops_key \
|
||||
--parallel 8 \
|
||||
hosts.txt > fleet-scan-$(date +%F).json
|
||||
@@ -95,7 +95,7 @@ Output shape:
|
||||
|
||||
### Larger fleet (>100 hosts)
|
||||
|
||||
`iamroot-fleet-scan.sh` is intentionally simple (parallel ssh). For
|
||||
`skeletonkey-fleet-scan.sh` is intentionally simple (parallel ssh). For
|
||||
fleets too large for SSH-fan-out, wrap it in your config-management
|
||||
tool of choice:
|
||||
|
||||
@@ -108,22 +108,22 @@ tool of choice:
|
||||
Sample Ansible task:
|
||||
|
||||
```yaml
|
||||
- name: scan with iamroot
|
||||
- name: scan with skeletonkey
|
||||
copy:
|
||||
src: iamroot
|
||||
dest: /tmp/iamroot
|
||||
src: skeletonkey
|
||||
dest: /tmp/skeletonkey
|
||||
mode: '0755'
|
||||
- name: run --scan --json
|
||||
command: /tmp/iamroot --scan --json --no-color
|
||||
command: /tmp/skeletonkey --scan --json --no-color
|
||||
register: scan
|
||||
changed_when: false
|
||||
failed_when: false # iamroot exit codes are semantic, not errors
|
||||
failed_when: false # skeletonkey exit codes are semantic, not errors
|
||||
- name: collect
|
||||
set_fact:
|
||||
iamroot_scan: "{{ scan.stdout | from_json }}"
|
||||
skeletonkey_scan: "{{ scan.stdout | from_json }}"
|
||||
- name: cleanup
|
||||
file:
|
||||
path: /tmp/iamroot
|
||||
path: /tmp/skeletonkey
|
||||
state: absent
|
||||
```
|
||||
|
||||
@@ -133,46 +133,46 @@ Sample Ansible task:
|
||||
|
||||
```
|
||||
# splunk input config (inputs.conf)
|
||||
[script:///opt/iamroot/iamroot-cron-scan.sh]
|
||||
[script:///opt/skeletonkey/skeletonkey-cron-scan.sh]
|
||||
interval = 86400
|
||||
source = iamroot
|
||||
sourcetype = iamroot:scan
|
||||
source = skeletonkey
|
||||
sourcetype = skeletonkey:scan
|
||||
```
|
||||
|
||||
`iamroot-cron-scan.sh`:
|
||||
`skeletonkey-cron-scan.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
/usr/local/bin/iamroot --scan --json --no-color
|
||||
/usr/local/bin/skeletonkey --scan --json --no-color
|
||||
```
|
||||
|
||||
Search the indexed events:
|
||||
|
||||
```spl
|
||||
index=iamroot sourcetype="iamroot:scan" modules{}.result=VULNERABLE
|
||||
index=skeletonkey sourcetype="skeletonkey:scan" modules{}.result=VULNERABLE
|
||||
| stats count by host modules{}.cve
|
||||
```
|
||||
|
||||
### Elastic / OpenSearch
|
||||
|
||||
Filebeat module reading the per-host scan JSON files (one per day),
|
||||
indexed into an `iamroot-*` index pattern. Standard Kibana
|
||||
indexed into an `skeletonkey-*` index pattern. Standard Kibana
|
||||
visualization on `modules.cve` over time tracks vulnerability lifecycle.
|
||||
|
||||
### Sigma → your platform
|
||||
|
||||
```bash
|
||||
# Ship Sigma rules into your platform
|
||||
iamroot --detect-rules --format=sigma > /etc/sigma/iamroot.yml
|
||||
skeletonkey --detect-rules --format=sigma > /etc/sigma/skeletonkey.yml
|
||||
# Convert to your target (Sentinel, Elastic, etc.) via sigmac
|
||||
sigmac -t elastic /etc/sigma/iamroot.yml
|
||||
sigmac -t elastic /etc/sigma/skeletonkey.yml
|
||||
```
|
||||
|
||||
## Day-to-day operational shape
|
||||
|
||||
### What "good" looks like in the SIEM
|
||||
|
||||
- Daily `iamroot --scan --json` from every host indexed
|
||||
- Daily `skeletonkey --scan --json` from every host indexed
|
||||
- Trend dashboard: count of VULNERABLE results by CVE over time
|
||||
- Goal: every VULNERABLE → OK transition within SLA (e.g., 14 days for
|
||||
patched-mainline bugs, 24h for actively-exploited)
|
||||
@@ -181,22 +181,22 @@ sigmac -t elastic /etc/sigma/iamroot.yml
|
||||
|
||||
### Auditd events from the embedded rules
|
||||
|
||||
After deploying `iamroot --detect-rules --format=auditd`:
|
||||
After deploying `skeletonkey --detect-rules --format=auditd`:
|
||||
|
||||
```bash
|
||||
# By module key
|
||||
sudo ausearch -k iamroot-copy-fail -ts today
|
||||
sudo ausearch -k iamroot-dirty-pipe -ts today
|
||||
sudo ausearch -k iamroot-pwnkit -ts today
|
||||
sudo ausearch -k iamroot-nf-tables-userns -ts today
|
||||
sudo ausearch -k iamroot-overlayfs -ts today
|
||||
sudo ausearch -k skeletonkey-copy-fail -ts today
|
||||
sudo ausearch -k skeletonkey-dirty-pipe -ts today
|
||||
sudo ausearch -k skeletonkey-pwnkit -ts today
|
||||
sudo ausearch -k skeletonkey-nf-tables-userns -ts today
|
||||
sudo ausearch -k skeletonkey-overlayfs -ts today
|
||||
|
||||
# Anything iamroot-tagged in the last hour
|
||||
sudo ausearch -k 'iamroot-*' -ts recent
|
||||
# Anything skeletonkey-tagged in the last hour
|
||||
sudo ausearch -k 'skeletonkey-*' -ts recent
|
||||
|
||||
# Forward to syslog (rsyslog example)
|
||||
# /etc/rsyslog.d/iamroot.conf:
|
||||
:msg, contains, "iamroot-" @@your-siem.example.com:514
|
||||
# /etc/rsyslog.d/skeletonkey.conf:
|
||||
:msg, contains, "skeletonkey-" @@your-siem.example.com:514
|
||||
```
|
||||
|
||||
### When a VULNERABLE result fires
|
||||
@@ -208,11 +208,11 @@ A scan reports VULNERABLE for module X
|
||||
│
|
||||
├── Q: Can I patch the underlying kernel / package?
|
||||
│ ├── YES → schedule patch window. In the meantime:
|
||||
│ │ iamroot --mitigate X (if supported)
|
||||
│ │ skeletonkey --mitigate X (if supported)
|
||||
│ │ Verify auditd rule for X is loaded.
|
||||
│ │ Monitor for the rule key.
|
||||
│ └── NO (legacy LTS, embedded device, prod freeze) →
|
||||
│ iamroot --mitigate X (essential)
|
||||
│ skeletonkey --mitigate X (essential)
|
||||
│ Compensating control: tighten LSM (SELinux/AppArmor)
|
||||
│ Document in risk register
|
||||
│
|
||||
@@ -238,7 +238,7 @@ If you applied a mitigation and now need to revert (e.g., the kernel
|
||||
patch has rolled out fleet-wide):
|
||||
|
||||
```bash
|
||||
sudo iamroot --cleanup copy_fail
|
||||
sudo skeletonkey --cleanup copy_fail
|
||||
# OR manually:
|
||||
sudo rm /etc/modprobe.d/dirtyfail-mitigations.conf
|
||||
sudo rm /etc/sysctl.d/99-dirtyfail-mitigations.conf
|
||||
@@ -249,11 +249,11 @@ sudo rm /etc/sysctl.d/99-dirtyfail-mitigations.conf
|
||||
|
||||
| Rule key | False positive | Fix |
|
||||
|---|---|---|
|
||||
| `iamroot-copy-fail-afalg` | strongSwan, libcrypto using kernel crypto | `-F auid=` exclude service account UIDs |
|
||||
| `iamroot-dirty-pipe-splice` | nginx, HAProxy, kTLS | `-F gid!=33 -F gid!=99` exclude web service accounts |
|
||||
| `iamroot-pwnkit-execve` | gnome-software, polkit's own re-exec | Correlate by parent process; pkexec via gnome dbus is benign |
|
||||
| `iamroot-nf-tables-userns` | docker rootless, podman, snap confined apps | Whitelist known userns-using service GIDs |
|
||||
| `iamroot-overlayfs` | docker / containerd mounting overlayfs as root | The rule is intended for unprivileged-userns overlayfs mounts; add `-F auid>=1000` |
|
||||
| `skeletonkey-copy-fail-afalg` | strongSwan, libcrypto using kernel crypto | `-F auid=` exclude service account UIDs |
|
||||
| `skeletonkey-dirty-pipe-splice` | nginx, HAProxy, kTLS | `-F gid!=33 -F gid!=99` exclude web service accounts |
|
||||
| `skeletonkey-pwnkit-execve` | gnome-software, polkit's own re-exec | Correlate by parent process; pkexec via gnome dbus is benign |
|
||||
| `skeletonkey-nf-tables-userns` | docker rootless, podman, snap confined apps | Whitelist known userns-using service GIDs |
|
||||
| `skeletonkey-overlayfs` | docker / containerd mounting overlayfs as root | The rule is intended for unprivileged-userns overlayfs mounts; add `-F auid>=1000` |
|
||||
|
||||
## Pre-patch quarantine pattern
|
||||
|
||||
@@ -261,13 +261,13 @@ If a CVE is in active exploitation and you can't patch immediately:
|
||||
|
||||
```bash
|
||||
# Stage 1: detect
|
||||
sudo iamroot --scan --json | jq '.modules[] | select(.cve == "CVE-XXXX")'
|
||||
sudo skeletonkey --scan --json | jq '.modules[] | select(.cve == "CVE-XXXX")'
|
||||
|
||||
# Stage 2: mitigate (where supported)
|
||||
sudo iamroot --mitigate <module>
|
||||
sudo skeletonkey --mitigate <module>
|
||||
|
||||
# Stage 3: monitor — auditd rules already deployed
|
||||
sudo ausearch -k 'iamroot-*' -ts today | grep <module>
|
||||
sudo ausearch -k 'skeletonkey-*' -ts today | grep <module>
|
||||
|
||||
# Stage 4: contain — temporarily restrict the trigger surface
|
||||
# e.g., for nf_tables CVE-2024-1086:
|
||||
@@ -281,7 +281,7 @@ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=1
|
||||
|
||||
## Maintenance contract
|
||||
|
||||
When IAMROOT ships a new module:
|
||||
When SKELETONKEY ships a new module:
|
||||
|
||||
1. CI test passes on at least one vulnerable + patched kernel pair
|
||||
2. Detection rules ship alongside (auditd + sigma minimum)
|
||||
@@ -293,7 +293,7 @@ Treat these as the SLA for any blue-team-facing deliverable.
|
||||
|
||||
## When you find a new false positive
|
||||
|
||||
File an issue at https://github.com/KaraZajac/IAMROOT/issues with:
|
||||
File an issue at https://github.com/KaraZajac/SKELETONKEY/issues with:
|
||||
- The exact ausearch line that fired
|
||||
- The legitimate process that produced it
|
||||
- Distro / kernel version
|
||||
|
||||
+13
-13
@@ -2,24 +2,24 @@
|
||||
|
||||
## Acceptable use
|
||||
|
||||
IAMROOT is intended for:
|
||||
SKELETONKEY is intended for:
|
||||
|
||||
1. **Authorized red-team / pentest engagements.** You have a written
|
||||
scope, signed by someone who can authorize testing on the target
|
||||
systems.
|
||||
2. **Defensive teams testing detection coverage.** You're using
|
||||
IAMROOT in a lab to verify your auditd/sigma/falco rules fire as
|
||||
SKELETONKEY in a lab to verify your auditd/sigma/falco rules fire as
|
||||
expected.
|
||||
3. **Security researchers studying historical LPEs.** You're reading
|
||||
the code, running it in your own VMs, learning how the primitives
|
||||
actually work end-to-end.
|
||||
4. **Build engineers verifying patch coverage.** You're running
|
||||
`iamroot --scan` against your fleet's golden images to confirm
|
||||
`skeletonkey --scan` against your fleet's golden images to confirm
|
||||
each known CVE shows up as patched.
|
||||
|
||||
## Not-acceptable use
|
||||
|
||||
IAMROOT should not be used:
|
||||
SKELETONKEY should not be used:
|
||||
|
||||
1. On systems you do not own and have not been authorized to test
|
||||
2. As part of unauthorized access to any system
|
||||
@@ -28,18 +28,18 @@ IAMROOT should not be used:
|
||||
4. To build a worm, scanner, or any tool that automatically targets
|
||||
systems at scale without per-target authorization
|
||||
|
||||
By using IAMROOT you assert that your use falls into the
|
||||
By using SKELETONKEY you assert that your use falls into the
|
||||
acceptable-use cases above.
|
||||
|
||||
## Why this is publishable
|
||||
|
||||
Every CVE bundled in IAMROOT is:
|
||||
Every CVE bundled in SKELETONKEY is:
|
||||
|
||||
- **Already patched** in upstream mainline kernel
|
||||
- **Already published** in NVD or distro security trackers
|
||||
- **Already covered** by existing public PoCs
|
||||
|
||||
IAMROOT does not introduce new offensive capability. It bundles,
|
||||
SKELETONKEY does not introduce new offensive capability. It bundles,
|
||||
documents, and CI-tests what is already public — and ships the
|
||||
detection signatures defenders need to spot it.
|
||||
|
||||
@@ -51,25 +51,25 @@ real defensive value through the detection-rule exports.
|
||||
|
||||
## Disclosure
|
||||
|
||||
If you find a bug in IAMROOT itself (incorrect detection, broken
|
||||
If you find a bug in SKELETONKEY itself (incorrect detection, broken
|
||||
exploit on a kernel where it should work, missing a backport in the
|
||||
range metadata): file a public GitHub issue.
|
||||
|
||||
If you find a **new 0-day kernel LPE while inspired by reading
|
||||
IAMROOT code**: please disclose it responsibly to the kernel
|
||||
SKELETONKEY code**: please disclose it responsibly to the kernel
|
||||
security team (`security@kernel.org`) and the affected distros
|
||||
*before* writing a public PoC. Once upstream patch ships and a CVE
|
||||
is assigned, IAMROOT will gladly accept the module.
|
||||
is assigned, SKELETONKEY will gladly accept the module.
|
||||
|
||||
## Persistence and stealth are out of scope
|
||||
|
||||
`--exploit-backdoor` in the copy_fail module overwrites a
|
||||
`/etc/passwd` line with a `uid=0` shell account. This is **overt**:
|
||||
|
||||
- The username is `iamroot` (was `dirtyfail`) — instantly identifiable
|
||||
- It's covered by the auditd rules IAMROOT ships
|
||||
- The username is `skeletonkey` (was `dirtyfail`) — instantly identifiable
|
||||
- It's covered by the auditd rules SKELETONKEY ships
|
||||
- `--cleanup-backdoor` restores the original line
|
||||
|
||||
If you're looking for evasion, persistence, or stealth: not here.
|
||||
Use a real C2 framework if you have authorization to do so. IAMROOT
|
||||
Use a real C2 framework if you have authorization to do so. SKELETONKEY
|
||||
stops at "demonstrate that the bug works."
|
||||
|
||||
+27
-27
@@ -1,20 +1,20 @@
|
||||
# IAMROOT — kernel offset resolution
|
||||
# SKELETONKEY — kernel offset resolution
|
||||
|
||||
The 7 🟡 PRIMITIVE modules each land a kernel-side primitive (heap-OOB
|
||||
write, slab UAF, etc.). The default `--exploit` returns
|
||||
`IAMROOT_EXPLOIT_FAIL` after the primitive fires — the verified-vs-claimed
|
||||
`SKELETONKEY_EXPLOIT_FAIL` after the primitive fires — the verified-vs-claimed
|
||||
bar means we don't claim root unless we empirically have it.
|
||||
|
||||
`--full-chain` engages the shared finisher (`core/finisher.{c,h}`) which
|
||||
converts the primitive to a real root pop via `modprobe_path` overwrite:
|
||||
|
||||
```
|
||||
attacker → arb_write(modprobe_path, "/tmp/iamroot-mp-<pid>.sh")
|
||||
→ execve("/tmp/iamroot-trig-<pid>") # unknown-format binary
|
||||
attacker → arb_write(modprobe_path, "/tmp/skeletonkey-mp-<pid>.sh")
|
||||
→ execve("/tmp/skeletonkey-trig-<pid>") # unknown-format binary
|
||||
→ kernel call_modprobe() # spawns modprobe_path as init
|
||||
→ /tmp/iamroot-mp-<pid>.sh runs as root
|
||||
→ cp /bin/bash /tmp/iamroot-pwn-<pid>; chmod 4755 /tmp/iamroot-pwn-<pid>
|
||||
→ caller exec /tmp/iamroot-pwn-<pid> -p
|
||||
→ /tmp/skeletonkey-mp-<pid>.sh runs as root
|
||||
→ cp /bin/bash /tmp/skeletonkey-pwn-<pid>; chmod 4755 /tmp/skeletonkey-pwn-<pid>
|
||||
→ caller exec /tmp/skeletonkey-pwn-<pid> -p
|
||||
→ root shell
|
||||
```
|
||||
|
||||
@@ -27,14 +27,14 @@ address) at runtime.
|
||||
non-zero value for each field:
|
||||
|
||||
1. **Environment variables** — operator override.
|
||||
- `IAMROOT_KBASE=0x...`
|
||||
- `IAMROOT_MODPROBE_PATH=0x...`
|
||||
- `IAMROOT_POWEROFF_CMD=0x...`
|
||||
- `IAMROOT_INIT_TASK=0x...`
|
||||
- `IAMROOT_INIT_CRED=0x...`
|
||||
- `IAMROOT_CRED_OFFSET_REAL=0x...` (offset of `real_cred` in `task_struct`)
|
||||
- `IAMROOT_CRED_OFFSET_EFF=0x...`
|
||||
- `IAMROOT_UID_OFFSET=0x...` (offset of `uid_t uid` in `cred`, usually 0x4)
|
||||
- `SKELETONKEY_KBASE=0x...`
|
||||
- `SKELETONKEY_MODPROBE_PATH=0x...`
|
||||
- `SKELETONKEY_POWEROFF_CMD=0x...`
|
||||
- `SKELETONKEY_INIT_TASK=0x...`
|
||||
- `SKELETONKEY_INIT_CRED=0x...`
|
||||
- `SKELETONKEY_CRED_OFFSET_REAL=0x...` (offset of `real_cred` in `task_struct`)
|
||||
- `SKELETONKEY_CRED_OFFSET_EFF=0x...`
|
||||
- `SKELETONKEY_UID_OFFSET=0x...` (offset of `uid_t uid` in `cred`, usually 0x4)
|
||||
|
||||
2. **`/proc/kallsyms`** — only useful when `kernel.kptr_restrict=0`
|
||||
OR you're already root. On modern distros (kptr_restrict=1 by
|
||||
@@ -60,18 +60,18 @@ non-zero value for each field:
|
||||
sudo grep -E ' (modprobe_path|init_task|_text)$' /proc/kallsyms
|
||||
|
||||
# Use the addresses inline:
|
||||
IAMROOT_MODPROBE_PATH=0xffffffff8228e7e0 \
|
||||
iamroot --exploit nf_tables --i-know --full-chain
|
||||
SKELETONKEY_MODPROBE_PATH=0xffffffff8228e7e0 \
|
||||
skeletonkey --exploit nf_tables --i-know --full-chain
|
||||
```
|
||||
|
||||
### Automated dump (preferred for upstreaming)
|
||||
|
||||
`iamroot --dump-offsets` walks the four-source chain itself and emits
|
||||
`skeletonkey --dump-offsets` walks the four-source chain itself and emits
|
||||
a ready-to-paste C struct entry on stdout:
|
||||
|
||||
```bash
|
||||
sudo iamroot --dump-offsets
|
||||
# /* Generated 2026-05-16 by `iamroot --dump-offsets`.
|
||||
sudo skeletonkey --dump-offsets
|
||||
# /* Generated 2026-05-16 by `skeletonkey --dump-offsets`.
|
||||
# * Host kernel: 5.15.0-56-generic distro=ubuntu
|
||||
# * Resolved fields: modprobe_path=kallsyms init_task=kallsyms cred=table
|
||||
# * Paste this entry into kernel_table[] in core/offsets.c.
|
||||
@@ -88,21 +88,21 @@ sudo iamroot --dump-offsets
|
||||
```
|
||||
|
||||
Paste the block into `kernel_table[]` in `core/offsets.c`, rebuild,
|
||||
and the new entry covers every IAMROOT user on that kernel. Open a
|
||||
and the new entry covers every SKELETONKEY user on that kernel. Open a
|
||||
PR to upstream it.
|
||||
|
||||
### Per-host (write System.map readable)
|
||||
|
||||
```bash
|
||||
sudo chmod 0644 /boot/System.map-$(uname -r)
|
||||
iamroot --exploit nf_tables --i-know --full-chain
|
||||
skeletonkey --exploit nf_tables --i-know --full-chain
|
||||
```
|
||||
|
||||
### Per-boot (lower kptr_restrict)
|
||||
|
||||
```bash
|
||||
sudo sysctl kernel.kptr_restrict=0
|
||||
iamroot --exploit nf_tables --i-know --full-chain
|
||||
skeletonkey --exploit nf_tables --i-know --full-chain
|
||||
```
|
||||
|
||||
Note: each of these requires root *once*. For a true non-root LPE on
|
||||
@@ -144,14 +144,14 @@ build + distro you tested against. Upstreamed entries make the
|
||||
|
||||
## Verifying success
|
||||
|
||||
The shared finisher (`iamroot_finisher_modprobe_path()`) drops a
|
||||
sentinel file at `/tmp/iamroot-pwn-<pid>` after `modprobe` runs our
|
||||
The shared finisher (`skeletonkey_finisher_modprobe_path()`) drops a
|
||||
sentinel file at `/tmp/skeletonkey-pwn-<pid>` after `modprobe` runs our
|
||||
payload. The finisher polls for this file with `S_ISUID` mode set
|
||||
for up to 3 seconds. Only when the sentinel materializes does the
|
||||
module return `IAMROOT_EXPLOIT_OK` and (unless `--no-shell`) exec
|
||||
module return `SKELETONKEY_EXPLOIT_OK` and (unless `--no-shell`) exec
|
||||
the setuid bash to drop a root shell.
|
||||
|
||||
If the sentinel never appears the module returns `IAMROOT_EXPLOIT_FAIL`
|
||||
If the sentinel never appears the module returns `SKELETONKEY_EXPLOIT_FAIL`
|
||||
with a diagnostic. Reasons it might fail even with offsets resolved:
|
||||
|
||||
- The arb-write didn't actually land (slab adjacency lost, value-pointer
|
||||
|
||||
Reference in New Issue
Block a user