dirty_pipe: fix 3 bugs — working CVE-2022-0847 exploit that lands real root
build / build (clang / debug) (push) Waiting to run
build / build (clang / default) (push) Waiting to run
build / build (gcc / debug) (push) Waiting to run
build / build (gcc / default) (push) Waiting to run
build / sanitizers (ASan + UBSan) (push) Waiting to run
build / clang-tidy (push) Waiting to run
build / drift-check (CISA KEV + Debian tracker) (push) Waiting to run
build / static-build (push) Waiting to run
build / build (clang / debug) (push) Waiting to run
build / build (clang / default) (push) Waiting to run
build / build (gcc / debug) (push) Waiting to run
build / build (gcc / default) (push) Waiting to run
build / sanitizers (ASan + UBSan) (push) Waiting to run
build / clang-tidy (push) Waiting to run
build / drift-check (CISA KEV + Debian tracker) (push) Waiting to run
build / static-build (push) Waiting to run
Verified out-of-band on a genuinely pre-fix mainline 5.16.0 kernel (installed
from kernel.ubuntu.com onto the jammy image; every cached cloud image was
pre-5.8 or backport-patched). `skeletonkey --exploit dirty_pipe` (uid 1000) ->
EXPLOIT_OK + a -rwsr-xr-x root:root bash, and /etc/passwd is byte-identical
afterward.
Three real bugs in the shipped module:
1. Wrong escalation: it flipped the CALLER's UID field to "0000" and ran
`su <self>`, which still demands the caller's password -> never rooted
anything.
2. False EXPLOIT_OK: it execlp'd su, so the dispatcher's "exec transferred ->
clean exit = OK" path reported success even on su's auth failure.
3. Dangerous revert: revert_passwd_page_cache() used drop_caches, which needs
root -> as an unprivileged caller it left the running system's /etc/passwd
page cache corrupted (broke sshd user resolution in testing).
Rewrite (AabyssZG-style, self-contained + verifying):
- overwrite ROOT's password field with a known $6$ crypt hash via the Dirty
Pipe primitive (the longer hash clobbers into following lines, transiently);
- authenticate as root over a pty with the matching password (su reads the
password from the controlling tty, not stdin);
- plant a root-owned proof + setuid bash; judge success ONLY by stat()'ing the
root-owned artifact;
- revert the page cache by writing the saved original bytes back through the
Dirty Pipe primitive itself — no root, no drop_caches, nothing persists.
cleanup() re-reverts idempotently. Unit harness green (33 + 115).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0118iUgHY44hdRtANgyCmu7y
This commit is contained in:
+27
-7
@@ -13,10 +13,13 @@ tamper check).
|
||||
|
||||
> **Headline finding:** the corpus was only ever *detect*-verified, never
|
||||
> *exploit*-verified. Running the exploits shows a mix of genuinely-working,
|
||||
> honestly-failing, and **falsely-succeeding** modules. Two flagship modules
|
||||
> reported `EXPLOIT_OK` while obtaining **no root at all** (`pwnkit`,
|
||||
> `ptrace_traceme`) — a false positive from the dispatcher's "execve
|
||||
> transferred → clean child exit = OK" path. Both are addressed below.
|
||||
> honestly-failing, and **falsely-succeeding** modules. Three modules reported
|
||||
> `EXPLOIT_OK` while obtaining **no root at all** (`pwnkit`, `ptrace_traceme`,
|
||||
> `dirty_pipe`) — a false positive from the dispatcher's "execve transferred →
|
||||
> clean child exit = OK" path (the exploit `execlp`'s a helper that then fails).
|
||||
> `dirty_pipe` additionally *corrupted the running system* (its unprivileged
|
||||
> `drop_caches` revert left /etc/passwd poisoned). All three are fixed below and
|
||||
> now either land real root or fail honestly.
|
||||
|
||||
## Confirmed landing root (uid=0 witnessed out of band)
|
||||
|
||||
@@ -31,6 +34,7 @@ tamper check).
|
||||
| `sudo_host` | CVE-2025-32462 | Ubuntu 22.04.0 / sudo 1.9.9 + host-restricted sudoers rule | works as shipped; `sudo -h <host>` → uid 0 (needs a host-scoped rule + resolvable host) |
|
||||
| `ptrace_traceme` | CVE-2019-13272 | Ubuntu 18.04.0 / 4.15.0-50 + pkexec + active-session polkit | **after a full rewrite** — `skeletonkey --exploit ptrace_traceme` (uid 1000) → root-owned setuid bash. See below |
|
||||
| `sudo_samedit` | CVE-2021-3156 | Ubuntu 18.04.0 / sudo 1.8.21p2 / libc-2.27 | **after a full rewrite** — Baron Samedit; `skeletonkey --exploit sudo_samedit` (uid 1000, non-sudoer) → root-owned setuid bash. See below |
|
||||
| `dirty_pipe` | CVE-2022-0847 | mainline 5.16.0 on Ubuntu 22.04 userspace | **after fixing 3 bugs** — `skeletonkey --exploit dirty_pipe` (uid 1000) → root-owned setuid bash; /etc/passwd byte-identical after revert. See below |
|
||||
|
||||
## Fixed this session
|
||||
|
||||
@@ -93,6 +97,23 @@ tamper check).
|
||||
Debian 64/49/60/214), and verifies root by `stat()`-ing the artifacts. Primary
|
||||
lengths landed first try; a `null_stomp_len` sweep (±8, the axis blasty's
|
||||
brute.sh perturbs) is the fallback for libc drift. Needs cc on the target.
|
||||
- **`dirty_pipe`** (CVE-2022-0847) — **three bugs fixed; now lands real root**
|
||||
(uid=0 witnessed out-of-band on a genuinely pre-fix **mainline 5.16.0** kernel —
|
||||
provisioned by installing the kernel.ubuntu.com 5.16.0 debs on the jammy image,
|
||||
since every cached cloud image was either pre-5.8 or backport-patched). The
|
||||
shipped exploit (1) flipped the *caller's* UID to `0000` and ran `su self`,
|
||||
which still demands the caller's password — it never rooted anything; (2)
|
||||
`execlp`'d su, so the dispatcher's exec-transfer path reported a **false
|
||||
`EXPLOIT_OK`** even on the auth failure; and (3) reverted with `drop_caches`,
|
||||
which needs root — so as an unprivileged caller it **left the running system's
|
||||
/etc/passwd page cache corrupted** (this actually broke sshd's user resolution
|
||||
in testing). Rewrote it to the reliable technique: overwrite **root's** password
|
||||
field with a known crypt hash, authenticate as root over a **pty** with the
|
||||
matching password (su reads the password from the controlling tty, not stdin),
|
||||
plant a root-owned proof + setuid bash, and **revert the page cache via the
|
||||
Dirty Pipe primitive itself** (write the saved original bytes back — no root, no
|
||||
drop_caches). Verified `/etc/passwd` is byte-identical afterward. Root judged
|
||||
only by the out-of-band artifact.
|
||||
- **`cgroup_release_agent`** — two real bugs fixed (commit `8c45b2b`): it read
|
||||
`getuid()` **after** `unshare(CLONE_NEWUSER)` (→ `65534`, so `uid_map` write
|
||||
was `"0 65534 1"` → EPERM), and it omitted `CLONE_NEWCGROUP` (→ cgroup-v1
|
||||
@@ -113,9 +134,8 @@ tamper check).
|
||||
|
||||
## Inconclusive (detect version-blind vs vendor backport)
|
||||
|
||||
| module | CVE | note |
|
||||
|---|---|---|
|
||||
| `dirty_pipe` | CVE-2022-0847 | Ubuntu 22.04.0's 5.15.0-25.25 almost certainly carries the backported fix (USN-5317); `detect()` is version-only so says VULNERABLE. Needs a genuinely pre-fix kernel (e.g. 5.13.0-19, or mainline 5.16.10) to verify the exploit. |
|
||||
*(none outstanding — `dirty_pipe` was here; now verified on a genuinely
|
||||
pre-fix mainline 5.16.0 kernel, see "Fixed this session".)*
|
||||
|
||||
## Kernel primitives — offset path fixed; `nf_tables` gap scoped (this session)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user