
macOS NFS क्लाइंट access-cache रेस (CVE-2026-43687) के लिए रिवर्स इंजीनियरिंग नोट्स और एक स्व-निहित PoC, जिसमें kext disassembly diff और dtrace रेस कैप्चर शामिल है।
macOS NFS क्लाइंट एक्सेस-कैश रेस (CVE-2026-43687) की स्वतंत्र रिवर्स इंजीनियरिंग, साथ ही एक कार्यशील PoC जो रेस को ट्रिगर करता है और dtrace के साथ इसे लाइव कैप्चर करता है।
यह बग _nfs_vnop_access में nfsnode एक्सेस-कैश पॉइंटर का एक अनसिंक्रोनाइज़्ड रीड है। एक दुर्भावनापूर्ण NFSv3 सर्वर एक्सेस कैश को पुनः आवंटित करने के लिए प्रेरित कर सकता है, जबकि एक अन्य थ्रेड इसे पढ़ रहा हो, जिससे एक कर्नेल मेमोरी डिस्क्लोज़र उत्पन्न होता है जिसे एक शत्रुतापूर्ण सर्वर प्रभावित कर सकता है। macOS 26.7 इसे nfsnode+0x158 पर एक lck_rw_t डालकर और कैश रीड के आसपास इसे शेयर्ड लेकर ठीक करता है।
| Field | Value |
|---|---|
| CVE | CVE-2026-43687 |
| Component | com.apple.filesystems.nfs (_nfs_vnop_access) |
| Affected | macOS Tahoe 26.6 और पहले, iOS 26.x और पहले |
| Patched in | macOS Tahoe 26.7, macOS Golden Gate 27, iOS 26.7, iOS 27 |
| Advisory impact | "Connecting to a malicious NFS server may disclose kernel memory." |
| CVSS v3.1 | 6.5 (Medium) — AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N |
| Reported by | R4mbb of KRsecurity और Peter Malone (Apple की एडवाइज़री के अनुसार) |
CVE-2026-43687 के लिए Apple की एडवाइज़री प्रभाव और पैच संस्करण का दस्तावेज़ीकरण करती है। यह तकनीकी तंत्र का दस्तावेज़ीकरण नहीं करती:
nfsnode में कौन सा फ़ील्ड रेस होता हैstat(2) के बजाय access(2) का उपयोग क्यों करना चाहिएलेखन के समय कोई सार्वजनिक तकनीकी राइटअप नहीं मिला। यह रिपॉज़िटरी 26.6 और 26.7 के बीच NFS kext के स्वतंत्र रिवर्स इंजीनियरिंग विश्लेषण और एक कार्यशील PoC के साथ उस अंतराल को भरती है जो लाइव टारगेट पर रेस को पुनरुत्पादित करता है।
यह एक खोज का दावा नहीं है। CVE की रिपोर्ट R4mbb और Peter Malone ने की और Apple ने इसे पैच किया। यहाँ योगदान तकनीकी विश्लेषण और पुनरुत्पादन है।
NFS क्लाइंट में _nfs_vnop_access nfsnode+0x158 पढ़ता है — प्रति-UID एक्सेस-कैश एरे का पॉइंटर — एक ही कॉल के भीतर दो बार, बिना कोई लॉक रखे:
; macOS 26.6, com.apple.filesystems.nfs
fffffe000b52def4 ldr w8, [x20, #0x160] ; count
fffffe000b52def8 cmp w23, w8
fffffe000b52defc b.ge ...
fffffe000b52df00 ldr x8, [x20, #0x158] ; cache ptr (read #1)
...
fffffe000b52df98 ldr x8, [x20, #0x158] ; cache ptr (read #2)
fffffe000b52dfb0 ldr w21, [x9] ; dereference
राइटर, _nfs_nget, जब भी क्लाइंट सर्वर से एक नया UID देखता है, kalloc_data के साथ एरे को पुनः आवंटित करता है और परिणाम को +0x158 पर संग्रहीत करता है:
fffffe000b52100c bl 0xfffffe000b6a1dd8 ; kalloc
fffffe000b521010 str x0, [x22, #0x158] ; cache ptr
fffffe000b521018 str w20, [x22, #0x160] ; cache count
यदि कोई अन्य थ्रेड रीडर के दो लोड के बीच उसी nfsnode पर _nfs_nget तक पहुँचता है, तो दूसरा लोड नया पॉइंटर लौटाता है जबकि पहला रीड — जो पहले से ही एक ऑफ़सेट की गणना के लिए उपयोग किया गया था — पुराने पर आधारित था। इसके बाद का डीरेफ़रेंस मुक्त कर्नेल हीप से पढ़ता है।
26.7 फिक्स:
fffffe000b9e3064 str x0, [x22, #0x168] ; cache ptr moved
fffffe000b9e306c str w28, [x22, #0x170] ; cache count moved
fffffe000b9e307c add x0, x22, #0x158 ; lock slot
fffffe000b9e3084 bl _lck_rw_init ; init RW lock
और _nfs_vnop_access में:
fffffe000b9f0200 add x0, x20, #0x158
fffffe000b9f0204 bl _lck_rw_lock_shared ; take the lock
fffffe000b9f0208 ldr x9, [x20, #0x168] ; cache pointer
...
fffffe000b9f0230 bl _lck_rw_unlock_shared ; release the lock
स्ट्रक्चर लेआउट परिवर्तन:
| Offset | macOS 26.6 | macOS 26.7 |
|---|---|---|
+0x158 | cache array pointer | lck_rw_t |
+0x160 | cache count | (part of lock) |
+0x168 | (other) | cache array pointer |
+0x170 | (other) | cache count |
_nfs_nget और _nfs_vnop_access का डिसअसेम्बली डिफ — देखें
docs/PATCH_DIFF.mdnfsnode+0x158+0x158 पर lck_rw_t डाला गया, कैश पॉइंटर +0x168 पर स्थानांतरित, रीड के आसपास lck_rw_lock_shared लिया गयाaccess(2) _nfs_vnop_access में प्रवेश करता है; stat(2) _nfs_getattr से होकर जाता है और भेद्य फ़ंक्शन तक कभी नहीं पहुँचतापूर्ण राइटअप के लिए docs/ANALYSIS.md और एड्रेस तथा लॉग नमूनों के लिए docs/ARTIFACTS.md देखें।
poc.sh — एक सिंगल-फ़ाइल, स्व-निहित PoC:
nfsd को रोकता है127.0.0.1 पर एक दुर्भावनापूर्ण NFSv3 सर्वर शुरू करता है जो प्रत्येक रिप्लाई में रिपोर्ट किए गए UID को बदलता हैnoac के साथ एक नए माउंटपॉइंट पर एक्सपोर्ट को माउंट करता हैtest -r / test -w के माध्यम से access(2) जारी करने वाला प्रति-उपयोगकर्ता हैमर स्पॉन करता हैnfs_vnop_access पर एक dtrace प्रोब अटैच करता है, किसी भी कॉल को रिकॉर्ड करता है जहाँ nfsnode+0x158 कॉल के बीच में बदलता है_nfs_vnop_access में अनसिंक्रोनाइज़्ड रीड एक ही कॉल के दौरान एरे को बदलते हुए देखता हैरेस डिस्क्लोज़र के लिए पूर्व शर्त है। रेस को वास्तविक लीक में बदलने के लिए, एक हमलावर को स्टेल पॉइंटर का उपयोग करते हुए रीडर को देखना होगा और उन बाइट्स को कहीं ऐसी जगह प्रसारित करना होगा जहाँ वे पढ़ सकें। arm64e पर, nfsnode+0x158 पर मान PAC-साइन्ड है और प्रति-बूट कुंजी यूज़रलैंड से उपलब्ध नहीं है, इसलिए अकेले dtrace रेस को देख सकता है लेकिन पॉइंटर को डिकोड नहीं कर सकता। docs/ANALYSIS.md में "Paths tested and ruled out" अनुभाग देखें।
प्रदर्शित प्रभाव रेस ही है — वह सटीक विंडो जिसे 26.7 RW-लॉक फिक्स बंद करता है।
dtrace उपलब्ध (कुछ इंस्टॉल पर SIP समायोजन की आवश्यकता हो सकती है)python3, dscl, mountPoC पूरी तरह से टारगेट पर चलता है। दुर्भावनापूर्ण NFS सर्वर 127.0.0.1 पर बाइंड करता है और माउंट लूपबैक पर होता है। यह PoC को नेटवर्क कॉन्फ़िगरेशन के बिना स्व-निहित और पुनरुत्पादनीय रखता है।
chmod +x poc.sh
sudo ./poc.sh
पर्यावरण चर के माध्यम से वैकल्पिक ट्यूनिंग:
sudo HAMMER_COUNT=30 RUN_SECONDS=600 ./poc.sh
HAMMER_COUNT प्रति-उपयोगकर्ता हैमर थ्रेड्स की संख्या निर्धारित करता है (जो भी nfsuserNNN खाते मौजूद हैं उनका उपयोग करता है, आवश्यकतानुसार उन्हें बनाता है)। RUN_SECONDS dtrace विंडो निर्धारित करता है।
[*] ensuring nfsuser accounts exist (UID 201..240)
nfsuser accounts available: 12
[*] starting evil NFS server on 127.0.0.1:2049
[*] mounting /Users/Shared/nfs_test (with noac)
mount check: hello.txt statable
[*] spawning up to 12 per-user threads + 1 root loop
started 12 user threads + 1 root loop
[*] running dtrace for 180s — looking for RACE lines
[*] stopping dtrace
[*] cleaning up
=================== SUMMARY ===================
RACE events caught: 16