
CVE-2026-46215 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, Linux DRM GEM change_handle ioctl में एक use-after-free। स्लैब रिक्लेमेशन और DirtyPipe-शैली पेज कैश ओवरराइट के माध्यम से अनप्रिविलेज्ड स्थानीय विशेषाधिकार वृद्धि को रूट तक प्रदर्शित करता है।
change_handle उपयोग-पश्चात-मुक्त (अविशेषाधिकार प्राप्त LPE)DRM कोर ioctl DRM_IOCTL_GEM_CHANGE_HANDLE (drm_gem_change_handle_ioctl, ioctl nr 0xD2) में उपयोग-पश्चात-मुक्त के माध्यम से स्थानीय विशेषाधिकार वृद्धि। किसी भी उपयोगकर्ता के लिए सुलभ जिसके पास रेंडर नोड (/dev/dri/renderD*, सभी प्रमुख डेस्कटॉप वितरणों पर systemd-logind द्वारा सक्रिय सत्र को प्रदान किया गया) तक पहुंच है। इस रिपॉजिटरी में श्रृंखला UAF को एक अविशेषाधिकार प्राप्त उपयोगकर्ता से पासवर्ड रहित रूट तक ले जाती है।
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)53096728b891 ("drm: Add DRM prime interface to reassign GEM handle", David Francis / AMD), AMD के CRIU कार्य के लिए जोड़ा गया5e28b7b94408) में। ioctl को अपस्ट्रीम 7.1 में इस और संबंधित रेस के कारण अक्षम भी किया जा रहा है।इस बग की पहली रिपोर्ट Puttimet Thammasaeng ने की थी, जो समाधान पर अपस्ट्रीम Reported-by क्रेडिट रखते हैं। मैंने इसे स्वतंत्र रूप से 2026-04-12 को [email protected] पर पाया और रिपोर्ट किया। मेरी रिपोर्ट स्वीकार की गई और अनुरक्षकों को अग्रेषित की गई, लेकिन पहले की रिपोर्ट को अपस्ट्रीम में श्रेय दिया गया है। इस रिपॉजिटरी में मेरा स्वयं का विश्लेषण और शोषण है।
Writeup: https://cyberstan.co.uk
समाधान जारी स्थिर कर्नेल (6.18.32 और 7.0.9 से आगे) में है। यह रिपॉजिटरी समाधानों के व्यापक रूप से उपलब्ध होने के बाद प्रकाशित की गई थी।
drm_gem_change_handle_ioctl() एक GEM ऑब्जेक्ट को एक हैंडल से दूसरे हैंडल में ले जाता है लेकिन कभी obj->handle_count को समायोजित नहीं करता। यह drm_vma_node_allow/revoke और ड्राइवर open/close कॉलबैक को भी छोड़ देता है। क्योंकि handle_count 1 पर रहता है, पुराने हैंडल पर एक समवर्ती GEM_CLOSE इसे 0 पर गिरा देता है और ऑब्जेक्ट को मुक्त कर देता है जबकि नया हैंडल अभी भी IDR में इसका संदर्भ देता है। वह लटकता हुआ हैंडल उपयोग-पश्चात-मुक्त है, बाद में drm_gem_object_release_handle() में डीरेफ़रेंस किया जाता है।
GEM_CHANGE_HANDLE को GEM_CLOSE के विरुद्ध रेस करें।pipe_buffer ऐरे से पुनः प्राप्त करें (kmalloc-512 को कंडीशन करने के लिए msg_msg फेंग शुई, फिर splice-भरे पाइप)।pipe_buf_ops लीक करें: obj->size (ऑफसेट 216) pipe_buf[5].ops के साथ ओवरलैप होता है, जो एक कर्नेल पॉइंटर और KASLR बेस देता है।obj->name (ऑफसेट 224) pipe_buf[5].flags पर पहुंचे और PIPE_BUF_FLAG_CAN_MERGE सेट करे (name = 16 = 0x10)।/etc/passwd है, रूट पासवर्ड रहित हो जाता है।ऑफसेट pahole के माध्यम से सत्यापित किए गए हैं और 6.18 से 7.0 तक लेआउट के लिए विशिष्ट हैं। अन्य कर्नेल के लिए GEM_* / PIPEBUF_* परिभाषाओं को ओवरराइड करें।
poc.c - शोषण। -lpthread के साथ स्थिर बनाएं।run_exploit.sh - PoC और एक न्यूनतम initramfs बनाता है, इसे QEMU में बूट करता है।qemu-system-x86_64, gcc, busybox (स्थिर), fakeroot, cpio, gzip। KVM (/dev/kvm) अनुशंसित है; इसके साथ रेस कहीं अधिक विश्वसनीय है।
शोषण को विशिष्ट विकल्पों के साथ बने एक कमजोर लक्ष्य की आवश्यकता है:
CONFIG_KASAN बंद होना चाहिए। KASAN मुक्त स्लैब को क्वारंटाइन करता है और पाइप-स्प्रे पुनर्प्राप्ति को अवरुद्ध करता है, इसलिए शोषण KASAN बिल्ड के विरुद्ध काम नहीं करेगा।virtio_gpu (डेमो द्वारा उपयोग) या nouveau।CONFIG_DRM=y, CONFIG_DRM_VIRTIO_GPU=y, CONFIG_DEVTMPFS=y, CONFIG_BLK_DEV_INITRD=y।चरण:
CONFIG_KASAN सेट नहीं है।make -j"$(nproc)" bzImage, जो arch/x86/boot/bzImage उत्पन्न करता है।डेमो nokaslr के साथ बूट होता है ताकि लीक किया गया पॉइंटर नियतात्मक हो। लीक अपने आप KASLR को पराजित करता है, इसलिए KASLR-चालू भी काम करता है, पता केवल प्रति बूट बदलता है।
drivers/gpu/drm/drm_gem.c फ़ाइल में drm_gem_change_handle_ioctl() फ़ंक्शन देखें।
त्वरित जांच:
awk '/^int drm_gem_change_handle_ioctl/,/^}/' \
drivers/gpu/drm/drm_gem.c | grep -c handle_count
0 का अर्थ है कमजोर (कोई रेफ़काउंट हैंडलिंग नहीं)।संरचना द्वारा:
file_priv->prime.lock लेता है, एक एकल idr_alloc(&file_priv->object_idr, obj, ...), फिर पुराने हैंडल पर idr_remove()। कोई drm_gem_object_handle_get नहीं।idr_alloc फिर idr_replace(NULL) पुराने हैंडल को अलग करने के लिए), वास्तविक ऑब्जेक्ट केवल तभी स्वैप किया जाता है जब प्राइम ऑपरेशन सफल होते हैं।./run_exploit.sh /path/to/bzImage
यह poc.c बनाता है, इसे initramfs में लपेटता है, और QEMU को -device virtio-gpu-pci और nokaslr के साथ बूट करता है। PoC uid 1000 (अविशेषाधिकार प्राप्त) के रूप में चलता है, फिर स्क्रिप्ट /etc/passwd को पहले और बाद में प्रिंट करती है और आपको एक शेल में छोड़ देती है। VM को poweroff -f या Ctrl-A X से छोड़ें।
[!] Race won (iter 977): handle=132049
[!] KASLR: pipe_buf_ops = 0xffffffff82428400
[!] EXPLOIT SUCCESSFUL
[!] FLINK: 16 = 0x10
[*] /etc/passwd:
root::0:0:pwned:/root:/bin/sh
[!] LPE CONFIRMED
[!] root account is now passwordless
रूट के स्वामित्व वाली एक केवल-पढ़ने योग्य (chmod 444) /etc/passwd एक अविशेषाधिकार प्राप्त प्रक्रिया द्वारा अधिलेखित की जाती है। root: पंक्ति अपना पासवर्ड फ़ील्ड खो देती है।
पैच किए गए ट्री (6.18.32, 7.0.9, 7.1-rc3 या बाद का, या कोई भी ट्री जो ऊपर पैच की गई जांच पास करता है) के विरुद्ध पुनर्निर्माण करें और फिर से चलाएं:
./run_exploit.sh /path/to/patched-bzImage
पैच किए गए कर्नेल के विरुद्ध शोषण को कभी "EXPLOIT SUCCESSFUL" तक नहीं पहुंचना चाहिए: रेस अब नए हैंडल के नीचे से ऑब्जेक्ट को मुक्त नहीं करता है, इसलिए लीक कभी भी एक वैध कर्नेल पॉइंटर नहीं लौटाता है।
अपस्ट्रीम समाधान शिप होने के बाद अनुसंधान और रक्षात्मक उद्देश्यों के लिए प्रकाशित। जैसा है वैसा प्रदान किया गया। केवल अपने नियंत्रण वाले VM के विरुद्ध चलाएं।