
PoC-Exploit für CVE-2023-41993. Er ist nur bis addrof/fakeobj implementiert. Die Zuverlässigkeit ist nicht besonders gut. Wenn du es verbessern möchtest, versuche, Structure-IDs zu sprayen.
https://po6ix.github.io/POC-for-CVE-2023-41993/pwn.html
Ich habe mich entschieden, es aufgrund mehrerer Anfragen mit GitHub Pages zu hosten.
Wünsch mir Glück, dass GitHub mich nicht blockiert ...
Das liegt daran, dass der in der pwn-Funktion definierte Faktorwert für dein Gerät nicht korrekt ist.
Für diesen Fall habe ich es so gemacht, dass ein zufälliger Wert zwischen 87 und 1088 verwendet wird.
Du kannst den korrekten Faktorwert also einfach durch gelegentliches Aktualisieren finden.
Es sollte mit hoher Wahrscheinlichkeit innerhalb von 100 Versuchen funktionieren.
Es wäre auch schön, wenn du mir die Informationen schicken könntest, die im Erfolgsfall angezeigt werden.
Das verschafft dir eine r/w-Primitive für den Safari-WebContent-Prozess.
Aber um es tatsächlich nutzbar zu machen, musst du es mit anderen Komponenten verketten.
Du möchtest vielleicht ein detailliertes Write-up dazu. Aber leider kann ich mir die Zeit nicht leisten, das Ding zu schreiben. Deshalb schreibe ich hier ein paar Notizen, damit du verstehst, wie das funktioniert.
Wenn du dir den Commit ansiehst, geht es um die Änderung für HeapLocation.
Es wurde ein neuer Faktor hinzugefügt, um zu wissen, ob die Knoten gleich sind oder nicht.
Er sagt uns, dass Knoten wie GetByOffset, MultiGetByOffset verwechselt werden könnten.
Aber eigentlich geht es nur um den Offset.
Nehmen wir an, es gibt zwei GetByOffset-Knoten mit unterschiedlichem Offset.
Einer von ihnen wird per CSE eliminiert und der übrig gebliebene wird stattdessen verwendet.
Es ist also im Grunde eine Offset-Verwechslung, aber sie verschafft dir keinen Zugriff auf einen beliebigen Offset.
Denn um solche Knotenarten per CSE zu behandeln, müssen sie von der LICMPhase gehoistet werden.
Knotenarten, die Schreiboperationen ausführen, dürfen in dieser Phase nicht gehoistet werden.
Daher passiert dieselbe Verwechslung nicht bei PutByOffset-, MultiPutByOffset-Knoten.
Wenn GetByOffset gehoistet wird, wird außerdem die Funktion safeToExecute aufgerufen, um zu prüfen, ob der Knoten ausgeführt werden darf, und erlaubt nur den Zugriff auf den Offset, der kleiner als die Speicherkapazität (inline/ool) ist.
Die Idee, dies auszunutzen, war also GetterSetter.
Wenn du Object.__defineGetter__ aufrufst, um eine Eigenschaft zu definieren, wird ein GetterSetter-Objekt erstellt, aber im Property-Storage gespeichert und ist normalerweise nicht zugänglich.
Aber mit dieser Offset-Manipulation, die du hast, kannst du darauf zugreifen.
Dann rufst du die Funktion Object auf, um eine Typverwechslung auszulösen.
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);
}
Es wird ein SymbolObject erstellt, das den GetterSetter als internen Wert hat. Und das ist falsch, denn dieser interne Wert von SymbolObject sollte eine Symbol-Instanz sein, nicht 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;
}
Wenn du dann den description-Getter aufrufst, wird eine String-Instanz zurückgegeben. Das ist eine Typverwechslung zwischen Symbol.m_privateName und GetterSetter.m_getter. Jedes Mal, wenn du diesen Getter aufrufst, wird das Referenzzähler-Feld von m_privateName.m_uid erhöht, das sich bei Offset 0x0 befindet. Das ist sehr nützlich, denn dieser Offset ist die Stelle, an der sich die Structure-ID der Getter-Funktion befindet. Indem du diese Funktion einige Male aufrufst, kannst du die Structure-ID der JSFunction-Instanz ändern. Ich habe einen anderen Typ vorbereitet, der viele Eigenschaften hat. Wenn du die Structure-ID dann so synchronisierst, dass sie damit übereinstimmt, kannst du einen OOB-Write in den Property-Storage durchführen. Das liefert direkt die addrof/fakeobj-Primitive.