В этом году мы с a1exdandy выступали на конференциях VolgaCTF и KazHackStan с докладом про Patch Diffing программ, написанных на Оbjective-С, и то, как с его помощью можно искать и находить 0-day и 1-day уязвимости в продуктах компании Apple. Видео выступления можно посмотреть по ссылке, а для чтения статьи добро пожаловать под кат. Binary Diffing – это процесс сравнения двух исполняемых файлов с целью найти между ними сходства и различия. Patch Diffing – это частный случай Binary Diffing, когда файл с уязвимостью сравнивается с исправленным файлом (файл с патчем). Так можно узнать, какие исправления внес разработчик, и понять, в чем же заключалась уязвимость. Несколько лет назад (а именно в 2013 году) вышла статья о Patch Diffing’е программ для Windows, к которой мы также приложили руку. Рекомендуем ознакомиться с ней, если эта тема вам интересна в связи с ОС Windows. В этой же статье мы расскажем про анализ программ для яблочных ОС. Основная задача Binary Diffing – отследить, какие изменения вносились в приложение. Для этого нужно взять старую версию и сопоставить ее с новой, чтобы увидеть, какие изменения добавил разработчик и как они реализованы. Давайте рассмотрим ситуации, в которых это может быть полезно: Существует миф, согласно которому сокрытие информации об исправлении уязвимостей повышает уровень безопасности. Однако у злоумышленников, как правило, уже есть развитая инфраструктура для анализа патчей, поэтому они быстро найдут интересующие их данные. Сокрытие уязвимостей усложняет работу тем, кто отвечает за защиту системы, потому что без полного доступа к информации нельзя понять, насколько важен и актуален тот или иной патч для компании. По этому вопросу очень рекомендуем ознакомиться со статьей "Rashomon of disclosure" от исследователя Halvar Flake, где ситуация рассматривается с разных сторон. Далеко не всегда обнаруженные уязвимости скрываются намеренно. Разработчики могут не придать особого значения некоторым недостаткам их продукта и исправить их без огласки, как обычный баг. Мы уверены, что это далеко не все ситуации, в которых можно использовать Binary Diffing и Patch Diffing, и в комментариях вы можете написать свои сценарии. От теории постепенно переходим к практике. Естественно, заниматься Binary Diffing вручную – это безумная затея. Уже давно был разработан обширный инструментарий, состоящих как из standalone программ, так и плагинов для популярных бинарных анализаторов: Diaphora, BinDiff, DarunGrim, YaDiff, rizzo, Realyze, Turbodiff, patchdiff2, rematch и других. 
Интерфейс BinDiff.  
Интерфейс Diaphora. На практике, мы в основном пользуемся только первыми двумя (они и представлены на скриншотах выше). Большинство из текущих инструментов, к сожалению, либо не радуют качеством работы, либо вовсе заброшены. Сейчас появляются и новые инструменты с использованием ML, но их качество оставляет желать лучшего, хотя в них и есть интересные идеи сопоставления бинарных файлов разных архитектур. Также хочется отдельно отметить недавно появившиеся инструменты для сопоставления open-source кода в бинарных файлах: Pigaios и Karta. Это очень интересное и полезное направление. Они набирают метрики по исходному коду и потом с их помощью сопоставляют функции в бинарных файлах. Это очень полезно в условиях отсутствия символов и статической линковки. Задача этих инструментов – сопоставить функции в одном файле с функциями в другом. Как правило, приложения для яблочных ОС пишут на Objective-C. Это объектно-ориентированное расширение для Си, базирующееся на парадигмах языка Smalltalk с собственным runtime’ом. Если мы откроем программу на Objective-C в каком-нибудь бинарном анализаторе (в нашем случае это IDA), то увидим примерно такую картину: функции на C/C++ будут иметь абстрактные имена, а функции на Objective-C будут названы очень красиво. (Сразу скажем, что ситуации с обфускацией кода мы не рассматриваем – это отдельная история.) Это облегчает работу реверс-инженеру: легко посмотреть какой объект вызывается, какой метод применяется, какие у него аргументы. Также эта символьная информация сильно упрощает работу с инструментами для сравнения бинарных файлов. Она позволяет быстро и с минимальным количеством ошибок сопоставить функции.  Когда символов нет, инструментам приходится применять внутренние алгоритмы анализа, эвристики и т.д. Естественно, они не всегда работают правильно. Наверное, это выступление (в формате 45 мин.) и эта статья не появились бы на свет, если бы за последнее время компания Apple несколько раз не удивила мировое исследовательское сообщество. Давайте рассмотрим эти кейсы. В мае 2019 года вышла iOS 12.3 для iPhone, где исправили уязвимость, с помощью которой можно было установить jailbreak. В июле того же года Apple выпустил версию 12.4, где jailbreak снова начал работать: сотрудники случайно удалили защитный патч. Исправленную версию выпустили только через месяц: все это время любое устройство на последней версии можно было спокойно подвергнуть операции jailbreak’а. Все это было обнаружено благодаря patch diffing’у.  О личном опыте работы с Patch diffing на конференции Hack in the Box в своем докладе Recreating an iOS 0-day jailbreak out of Apple’s security patches рассказывал исследователь безопасности Stefan Esser. Он рассказал о CVE-2019-7278 и CVE-2019-7286 – они примечательны тем, были обнаружены благодаря подразделению Google Threat Analysis Group во время их реального использования злоумышленниками (ITW, in the wild). Apple получила о них информацию, но ни они, ни ребята из Google не раскрыли технических деталей. Тут Стефану и стало интересно, что же использовали злоумышленники. Мы очень рекомендуем ознакомиться с его докладом, так как в нем рассказывается и о процессе сравнения kernel и user space компонентов.
Занимательный факт: за день или за два до выступления Стефана, Ian Beer из Project Zero опубликовал цикл статей "A very deep dive into iOS Exploit chains found in the wild", чем немного спутал карты исследователю ;) Устройства Apple используют два загрузчика: Bootrom и Iboot. Первый начинает работать, как только включается устройство. Он находится в read-only memory, и его невозможно обновить. Iboot – это загрузчик второго уровня, улучшающий аутентификацию, которая выполняется в цепочке загрузки.
Особенность загрузчиков в том, что они разделяют код. В начале прошлого года специалисты сравнивали обновления для Iboot и обнаружили, что компания Apple исправила уязвимость в USB стеке. Зная, что этот же код используется в необновляемой части Bootrom, они поняли, что это уязвимость существует и там. Тогда началась разработка эксплойта checkm8. Сейчас все устройства на базе чипов от A5 до А11 (с iPhone 4s до iPhone X) полностью уязвимы, и на них возможно запустить свой код. При этом на устройствах версии XR и 11 данная уязвимость уже закрыта, и никаких сообщений об этом от Apple не было.
Итак, зная, где что запатчили и как разделяется код, можно находить очень крутые штуки! 
В мае Apple выпустила новое обновление для своих устройств. Мы посмотрели список исправленных уязвимостей и обнаружили утилиту sysdiagnose. Эта программа собирает диагностическую информацию из различных источников и в виде архива передает ее пользователю для дальнейшего анализа, поддержки или сервисного центра. Казалось, что повлиять на то, каким образом она собирает информацию, было невозможно. В описании патча было указано, что уязвимость может быть использована для повышения привилегий и является ошибкой нарушения целостности памяти (memory corruption), что еще больше нас заинтриговало. А сам автор находки так никакого описания от себя и не опубликовал ...  Вызвать sysdiagnose на macOS можно сочетанием клавиш “Сtrl” + “Option” + “Shift” + “.”, либо с помощью команды “sudo sysdiagnose”. На устройствах iOS нужно зажать клавишу включения и клавиши громкости вверх и вниз. Если вы хотите провести Patch Diffing устройств Apple, вам нужно получить две версии обновления (старое и новое). На macOS можно анализировать файлы в Software Update Catalog. Этот способ не всегда помогает, так как Apple удаляет некоторые обновления. Также обновления можно найти в директории /Library/Updates. Найти прошивку для iOS устройств можно на сайте ipsw.me.
Для macOS необходимо извлечь исполняемые файлы, файлы библиотек, фреймворков и ядро с помощью утилиты Suspicious Package.
Для iOS-устройств все немного сложнее. Само обновление представляет собой ZIP-архив. В этом архиве необходимо найти файл kernelcache: внутри находится ядро и его расширение. Однако перед анализом нужно конвертировать файл в формат Mach-O. Сделать это можно с помощью утилит img4tool, joker, jtool2 и других.
User-land-составляющие прошивки можно найти в самом большом DMG-образе: в нем находятся корневая система и исполняемые файлы. Однако если посмотреть на разделяемые библиотеки и фреймворки, то их там не будет. Дело в том, что для оптимизации пространства они помещаются в dyld_shared_cache. Этот файл вмещает в себя все библиотеки на iPhone и занимает больше 1 гигабайта, что усложняет его анализ. Если нужно скачать отдельную часть ipsw-файла, воспользуйтесь утилитой ipsw (https://github.com/blacktop/ipsw). Для изучения патча CVE-2019-8574 мы получаем файл из Library/Updates и извлекаем два исполняемых файла sysdiagnose (старый и новый) с помощью Suspicious Package, а затем проверяем обновление через утилиты для сравнения бинарных кодов, например, Diaphora или BinDiff (они гораздо лучше hex-редактора).  Для архитектуры x86_64 мы нашли лишь одну измененную функцию. В ARM изменений больше, но они незначительны.  Проблема заключалась в методе [SDTask start]: в нем добавили новую проверку. На рисунке можно увидеть, как происходит вызов некоторой функции isAppleInternal и проверяется наличие подстроки /usr/local в некотором пути.  Как было сказано ранее, sysdiagnose собирает информацию из различных источников. В качестве источников могут выступать как файлы (например, журнал событий), так и вывод каких-либо информационных команд (например, вывод списка процессов или потоков с помощью ps) – такие задачи хранятся в sysdiagnose в виде объектов SDTask. Метод [SDTask start] запускает выполнение задач. На рисунке ниже можно увидеть код, в котором все задачи формируются. Все пути исполняемых файлов и их аргументы прописаны прямо в программе, при этом встречаются задачи из различных директорий, в том числе из /usr/local.  Таким образом, до установки патча любая задача могла быть выполнена независимо от того, где находится исполняемый файл. После установки патча вызывается функция isAppleInternal, которая проверяет, является ли устройство внутренним тестовым устройством Apple. Если это внутреннее устройство, то выполнение задачи происходит стандартным образом, а если клиентское, то проверяется наличие подстроки /usr/local/ в пути исполняемого файла задачи. Если подстрока обнаружена, задача выполнена не будет. Напомним, что в описании уязвимости было сказано, что это ошибка нарушения целостности памяти (memory corruption), но в патче мы не заметили ничего подобного… На наш взгляд, это хорошо и стабильно эксплуатируемая логическая уязвимость. Для эксплуатации уязвимости необходимо выполнить следующие шаги:
Создать любой исполняемый файл (бинарный или shell script), который sysdiagnose запускает из /usr/local/bin, и поместить туда нашу полезную нагрузку. Список допустимых файлов приведен ниже. Важно, чтобы на системе был установлен пакетный менеджер brew: он позволяет пользователю без повышения привилегий создавать файлы в /usr/local/. Необходимо вызвать sysdiagnose. При этом наша полезная нагрузка будет выполнена с правами пользователя root, а ее вывод будет помещен в архив, который формирует sysdiagnose. Таким образом, мы получаем повышение привилегий. Однако, сам запуск sysdiagnose требует повышенных привилегий. Но, как говорилось выше, запустить sysdiagnose можно также с помощью сочетания клавиш, и это работает даже с экрана блокировки macOS.
Эта уязвимость может использоваться для создания различных вредоносных программ. Интересно, что с пакетным менеджером brew, а точнее с возможностью записи в /usr/local/ без привилегий, могут быть связаны и другие уязвимости. Данным исследованием нам бы хотелось показать важность и критичность patch diffing не только для атакующих, но и для повышения безопасности программных продуктов. К сожалению, патч может не только закрыть уязвимость, но и сыграть на руку атакующему. Тем не менее, этот подход позволяет не только изучать известные уязвимости, но и искать подобные им новые. Поэтому мы считаем, что patch diffing – это must have для каждого исследователя.