Играем с огнем: запускаем произвольный код на девелоперском iPhone 7
Под Новый год к нам в руки попал программатор JC PCIE-7. В процессе использования выяснилось, что его функционал ограничен, однако вещица оказалась с двойным дном. Внутри этого программатора мы обнаружили плату iPhone 7 специальной отладочной версии. За новогодними приключениями в мире исследования и отладки «яблочной» продукции – добро пожаловать под кат!
Наверное, только совсем ленивые исследователи безопасности не пощупали своими руками возможности, предоставляемые checkm8 и основанном на нем checkra1n (классный обзор checkm8 был недавно на Хабре). Конечно, мы тоже протестировали наши смартфоны и в процессе решили посмотреть, что есть полезного для исследователей на китайском рынке «железных» поделок. Интересный факт: если хочешь протестировать на безопасность какое-либо устройство, то на AliExpress уже есть для этого свои игрушки.
Наша игрушка пришла под самый Новый год – программатор JC PCIE-7, который легко найти на AliExpress:
Мы предполагали, что с помощью этого программатора сможем сделать бэкап флеш-памяти iPhone. Но при эксплуатации обнаружилось, что такой функции в программе просто нет! Да, программатор умеет читать и записывать конфигурацию во флеш-память и привязывать ее к телефону, но чтение произвольных блоков не поддерживается. Затем мы заметили необычные строки в консольном окне управляющей программы, похожие на журнал загрузки операционной системы:
Кадр момента загрузки из обучающего видео
Наконец, мы обратили внимание на функцию подключения программатора к компьютеру по протоколу DFU. В этом режиме программатор корректно определялся в iTunes как iPhone. Мы решили разобрать устройство и посмотреть, что же находится внутри и как происходит эмуляция режима DFU.
Внутри программатора установлены три платы:
- управляющая плата на базе STM32F103;
- плата-мультиплексор с напаянным чипом NVMe и колодкой;
- и, неожиданно, плата iPhone 7.
Оказалось, что программатор переключает мультиплексор таким образом, что сначала плата iPhone загружается с напаянной флеш-памяти, а затем к ней подключается чип NVMe, который нужно прошить. Управление выполняется по виртуальному COM-порту, причем сначала передаются команды переключения чипов NVMe, а затем плата пробрасывает отладочную консоль iPhone, имитируя DCSD-кабель. Внимательно просмотрев отправляемые в COM-порт команды, мы увидели строчки:
setenv diags-path /AppleInternal/Diags/bin/diag.img4
diags
Уже на этом моменте мы поняли, что плата iPhone 7 не совсем обычная (в ней, как минимум, есть папка AppleInternal). Чтобы посмотреть, как она себя ведет в более привычном «окружении», мы переставили плату и NVMe чип в донорский телефон:
Помимо шестеренки при загрузке, в отладочной консоли мы увидели строчку BUILD_STYLE: DEVELOPMENT:
После окончательной загрузки (с помощью упомянутых ранее команд diags) мы получили фиолетовый экран, а уже после нажатия клавиш громкости на экране отобразилось следующее меню:
То есть собранное нами устройство оказалось специальной версией iPhone для тестирования и разработки! К слову, подобные телефоны скупаются исследователями и даже коллекционерами – вот это настоящий подарок в нашу техническую коллекцию! В полноценном девелоперском iPhone установлена специальная сборка iOS с рутовой консолью, значительно облегчающая исследование телефона и поиск уязвимостей:
В нашем случае на телефоне установлена только диагностическая утилита, основное взаимодействие с которой осуществляется через отладочную консоль:
Как можно видеть, функционал отладочной программы немаленький. Предназначена она в первую очередь для тестирования оборудования – большинство команд отвечают за тестирование различных аппаратных подсистем устройства. Наиболее полезными для нас оказались команды directory, memrw, memory и cat.
Выполнив команду directory, можно ознакомиться с содержимым файловой системы:
Выполнив команду cat -h, можно прочитать любой файл:
Комбинируя эти две команды, мы считали все файлы с устройства (почти 150 МБ):
Среди файлов нашлась «пасхалка», рекомендующая не шутить с Iphone7 огнем:
Выполнив команду memory -dump, можно читать диапазоны ОЗУ:
Наконец, с помощью команды memrw можно перезаписывать ОЗУ:
Несмотря на то, что чтение памяти это уже в меру полезный функционал, мы решили проверить: можно ли перезаписать области исполняемого кода и запустить что-то свое? Ведь iPhone девелоперский, а функционал пока что пользовательский – так не пойдет!
Стандартными способами сделать это не удалось, возникала ошибка с достаточно подробным трейс-логом:
В данном случае в трейс-логе сообщается, что при исполнении по адресу 0x87BF68E30 в модуле TestDirectMemoryRW.macho возникло исключение из-за записи в read-only адрес памяти. Но нет преград для технических исследователей под Новый год! Обратим внимание на самый последний вызов в стеке: DxeMain.macho. Присутствие аббревиатуры DXE и строки EDK Boot Loader commands позволяет сделать вывод, что на устройстве запущена EFI-совместимая система. И действительно, образ системы diags.img4 корректно открывается с помощью UEFITool:
Дизассемблирование программных модулей показало, что все исполняемые модули используют соглашения о вызовах EFI и стандартные структуры EDK:
Каждой команде терминала соответствует отдельный EFI-модуль, например, команде memory соответствует модуль TestUtilities.macho, который загружается в ОЗУ по адресу 0x87BE10000 (определено из трейс-лога).
Попробуем модифицировать программный код этого модуля, чтобы исполнить наш код.
По адресу 0x87BE1600D располагается текстовая строка, выводимая по команде memory –info, модифицируем ее:
Чтобы модифицировать нужную нам область памяти, требуется изменить права доступа на страницу по адресу 0x87BE14000 с read-only/execute на read/write/execute. Согласно документации AArch64, за права доступа отвечает специальная таблица трансляции. Таблица должна располагаться в ОЗУ и состоять из записей по 8 байт следующего формата:
Таблица трансляции второго уровня была обнаружена по адресу 0x87FBF4000, в диапазоне памяти, доступном для перезаписи. На этом уровне каждая запись соответствует 32 МБ (0x2000000) адресного пространства, поэтому нужная нам запись для диапазона 0x87A000000–0x87BFFFFFF находится по адресу 0x87FBF4000 + (0x87A000000 / 0x2000000) * 8 = 0x87FBF61E8:
Запись имеет тип table, то есть указывает на таблицу трансляции третьего уровня по адресу 0x87FCEC000. В этой таблице каждая запись соответствует одной 16 Кб (0x4000) странице адресного пространства и имеет тип block. Нужная нам запись для страницы 0x87BE14000 находится по адресу 0x87FCEC000 + (0x87BE14000 – 0x87A000000) / 0x4000 * 8 = 0x87FCEFC28:
Поскольку эта запись имеет тип block, она указывает на физическую страницу памяти и содержит атрибуты доступа. Нас интересует Lower block attributes, а именно поле Data Access Permissions bits:
Чтобы разрешить запись в страницу, устанавливаем значение этого поля в 0:
После этого у нас должен появиться доступ на запись для нужной области. После внесения требуемых изменений получаем желаемый результат:
The moral: с помощью описанного метода мы можем модифицировать любые системные приложения и исполнять произвольный код в среде UEFI на девелоперском iPhone.
Практической ценности для старых версий iPhone данное исследование не несет, но было весело разобраться в чем-то неизведанном и редком, особенно когда оно неожиданно появилось как подарок на Новый год.
P. S. А вот для исследования последних версий iPhone и получения SecureROM может пригодиться описанный метод: уязвимость checkm8 в них исправлена, а подобные «программаторы» на AliExpress на базе iPhone XR и iPhone 11 уже продаются.
Raccoon Security – специальная команда экспертов НТЦ «Вулкан» в области практической информационной безопасности, криптографии, схемотехники, обратной разработки и создания низкоуровневого программного обеспечения.
Источник