
Exploit de prueba de concepto para CVE-2023-41993, una confusión de tipo JIT en WebKit en Safari. Proporciona primitivas addrof/fakeobj mediante manipulación del heap y confusión GetterSetter, permitiendo lectura/escritura arbitraria en el proceso WebContent.
PoC exploit para CVE-2023-41993. Está escrito solo hasta addrof/fakeobj. La fiabilidad no es muy buena. Si quieres mejorarlo, intenta hacer spray de structure IDs.
https://po6ix.github.io/POC-for-CVE-2023-41993/pwn.html
He decidido alojarlo con GitHub Pages por las múltiples solicitudes.
Deséenme suerte para que GitHub no me bloquee...
Es porque el valor del factor definido en la función pwn no es correcto para tu dispositivo.
Para ese caso, hice que usara un valor aleatorio entre 87 y 1088.
Así que puedes encontrar el valor correcto del factor con solo refrescar en algún momento.
Debería funcionar dentro de 100 intentos probabilísticamente.
También sería bueno que me enviaras la información que se muestra en el caso de éxito.
Esto te da una primitiva de lectura/escritura (r/w) para el proceso webcontent de Safari.
Pero para que sea realmente útil, necesitarás encadenarlo con otros componentes.
Puede que quieras un informe detallado de esto. Pero desafortunadamente no tengo tiempo para escribirlo. Así que escribo algunas notas aquí para que puedas entender cómo funciona.
Si ves el commit, se trata del cambio para HeapLocation.
Se ha añadido un nuevo factor para saber si los nodos son iguales o no.
Nos dice que nodos como GetByOffset, MultiGetByOffset podrían confundirse.
Pero en realidad solo se trata del offset.
Digamos que hay dos nodos GetByOffset que tienen diferentes offsets.
Uno de ellos va a ser CSEed y el sobrante se usará en su lugar.
Así que básicamente es una confusión de offset, pero no te da acceso a un offset arbitrario.
porque para CSEar ese tipo de nodos necesitan ser elevados por LICMPhase.
Los tipos de nodos que realizan operaciones de escritura no están permitidos para ser elevados en esta fase.
Así que la misma confusión no ocurre con los nodos PutByOffset, MultiPutByOffset.
Además, cuando GetByOffset es elevado, se llama a la función safeToExecute para ver si el nodo es legítimo de ejecutar y solo permite acceder al offset menor que la capacidad de almacenamiento (inline/ool).
Así que la idea para explotar esto fue GetterSetter.
Si llamas a Object.__defineGetter__ para definir una propiedad, se crea un objeto GetterSetter pero se almacena en el property storage y esto no es accesible normalmente.
Pero puedes hacerlo con esta manipulación de offset que tienes.
Entonces llamas a la función Object para desencadenar una confusión de tipos.
JSObject* JSCell::toObjectSlow(JSGlobalObject* globalObject) const
{
Integrity::auditStructureID(structureID());
ASSERT(!isObject());
if (isString())
return static_cast<const JSString*>(this)->toObject(globalObject);
if (isHeapBigInt())
return static_cast<const JSBigInt*>(this)->toObject(globalObject);
ASSERT(isSymbol());
return static_cast<const Symbol*>(this)->toObject(globalObject);
}
Creará un SymbolObject que tiene el GetterSetter como valor interno. Y esto es incorrecto, porque se supone que este valor interno de SymbolObject es una instancia de Symbol, no un GetterSetter.
let getterSetter = jitme(1);
let symbolObject = Object(getterSetter);
symbolObject.description; // call the getter
String Symbol::description() const
{
auto& uid = m_privateName.uid();
return uid.isNullSymbol() ? String() : uid;
}
Entonces, cuando llamas al getter description, devolverá una instancia de String. Esto es una confusión de tipos entre Symbol.m_privateName y GetterSetter.m_getter. Cada vez que llamas a este getter, incrementa el campo de contador de referencias de m_privateName.m_uid, que está en el offset 0x0. Esto es muy útil, porque este offset es donde está el structure ID de la función getter. Llamando a esta función algunas veces, puedes cambiar el structure ID de la instancia de JSFunction. He preparado otro tipo que tiene muchas propiedades. Entonces, si sincronizas el structure ID para que sea el mismo que el de este, puedes hacer una escritura OOB al property storage. Esto da directamente la primitiva addrof/fakeobj.