dirty_cow: verify end-to-end on 4.8.0; robust su helper (dirty_pipe too)
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 the dirty_cow fix end-to-end on a genuinely Dirty-COW-vulnerable
mainline 4.8.0 kernel (installed the kernel.ubuntu.com 4.8.0 deb on a 16.04
image + virtio-rng for entropy). A standalone built verbatim from the module's
primitive + escalation raced root's password field, authenticated as root over
a pty, planted a root-owned setuid bash, and left /etc/passwd byte-identical.
(The full skeletonkey binary won't compile on xenial's 4.4-era uapi headers —
unrelated nft_* modules use newer kernel constants — so the verbatim standalone
stands in for --exploit dirty_cow there.)

Robustness fix (both dirty_cow AND dirty_pipe): the su-over-pty step now POLLS
for the password prompt and hard-caps at 20s. The previous fixed-delay write
raced su's prompt setup and HUNG on xenial; without a cap that would block the
revert and leave /etc/passwd poisoned. Now su can never hang the module, and the
revert always runs.

Also: netfilter_xtcompat now includes <linux/if.h> for IFNAMSIZ (ip_tables.h
doesn't pull it transitively on older kernel headers, e.g. Ubuntu 16.04) — a
real portability fix surfaced by building on xenial.

Host build + 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:
KaraZajac
2026-07-23 23:26:12 -04:00
parent 68dac6c063
commit 65588599ff
5 changed files with 63 additions and 26 deletions
+14 -4
View File
@@ -35,6 +35,7 @@ tamper check).
| `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 |
| `dirty_cow` | CVE-2016-5195 | mainline 4.8.0 on Ubuntu 16.04.7 | **after fixing the false-OK** — verbatim-module standalone (uid 1000) → root-owned setuid bash; /etc/passwd byte-identical after revert. See below |
## Fixed this session
@@ -123,9 +124,18 @@ tamper check).
`$6$` hash → authenticate as root over a pty → plant a root-owned proof + setuid
bash → revert by racing the original bytes back through the Dirty COW primitive.
Also fixed a latent buffer overflow (the success-check `readback[16]` was too
small for a >16-byte payload). The escalation half is byte-for-byte the verified
`dirty_pipe` technique; end-to-end root here needs a pre-4.8.3 kernel (every
cached VM is patched), so on those it now fails **honestly** with no corruption.
small for a >16-byte payload), and made the `su`-over-pty step **poll for the
prompt with a hard 20s cap** — a fixed-delay write raced su's prompt setup and
**hung on xenial**, which (without the cap) would have blocked the revert and
left /etc/passwd poisoned. The same robust `su` helper was back-ported to
`dirty_pipe`. **Verified end-to-end on a genuinely Dirty-COW-vulnerable
mainline 4.8.0 kernel** (provisioned by installing the kernel.ubuntu.com 4.8.0
deb on a 16.04 image + a virtio-rng for entropy): a standalone built verbatim
from the module's primitive + escalation + robust su raced root's passwd field,
authenticated as root, planted a root-owned setuid bash, and left /etc/passwd
byte-identical. (The full `skeletonkey` binary won't compile on xenial's 4.4-era
uapi headers — several unrelated `nft_*` modules use newer kernel constants — so
the verbatim standalone stands in for `--exploit dirty_cow` on that box.)
- **`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
@@ -147,7 +157,7 @@ every `exec*`-calling module:
| module | verdict | why |
|---|---|---|
| `dirty_cow` | ❌ **false-OK → fixed** | raced own UID + `su self`; `execlp(su)` transfer = OK. Fixed (see above). |
| `dirty_cow` | ❌ **false-OK → fixed + verified** | raced own UID + `su self`; `execlp(su)` transfer = OK. Fixed + verified end-to-end on mainline 4.8.0 (see above). |
| `pwnkit` | ✅ fixed earlier | now re-injects gconv + verifies |
| `ptrace_traceme` | ✅ fixed earlier | rewritten; verifies OOB artifact |
| `dirty_pipe` | ✅ fixed earlier | rewritten; verifies OOB artifact |