Играем с огнем: запускаем произвольный код на девелоперском 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 – специальная команда экспертов НТЦ «Вулкан» в области практической информационной безопасности, криптографии, схемотехники, обратной разработки и создания низкоуровневого программного обеспечения.
Источник
NANDкромантия: трансплантация флэш-памяти наживую
Нередко при анализе встроенной системы происходят преднамеренные, а иногда и нет, изменения, которые приводят к тому, что целевая система выходит из строя и впадает в так называемое состояние «кирпича». В некоторых случаях для ее реанимации достаточно выполнить сброс к заводским настройкам, в иных же приходится прошивать систему при помощи интерфейса отладки (JTAG/SWD/*) или вручную через внешнее устройство памяти (SPI/NOR/Nand/eMMC). В данной статье мы рассмотрим весьма креативный метод «раскирпичивания» системы после подобного сбоя.
Как-то в свободное время я возился с памятью маршрутизатора, где в качестве загрузчика использовалась прошивка CFE (Common Software Environment). В ходе моего взаимодействия с CFE в попытке определить аргументы, передаваемые при загрузке в ОС устройства, конфигурация системы была случайно повреждена:
В тот момент я понял, что для сохранения этих случайных изменений осталось выполнить заключительную операцию сохранения/записи, поэтому решил просто физически перезапустить устройство, чтобы избежать внесения изменений. После перезапуска возникла ошибка:
Когда маршрутизатор снова заработал, то тут же выдал вышеприведенную ошибку и в CFE не вошел. Без возможности обратиться к загрузчику конфигурацию изменить было нельзя, а процесс его восстановления тоже не представлялся возможным. Онлайн-поиск решения по этой ошибке ничем не помог и привел в тупик. Единственным выводом было, что при повреждении CFE подобным образом устройство впадает в состояние «кирпича». Тогда я переключился на работу с резервным девайсом, чтобы получить ответ на изначальный вопрос по интересовавшим меня аргументам. К слову говоря, установка kernp mfg_nvram_mode=1 mfg_nvram_url=BADURL была особенно интересна.
Позже я вернулся к своему «кирпичу», чтобы найти способ его восстановления. В нем используется Broadcom SoC, и, как выяснилось, доступ к JTAG обеспечивается через нераспаянные контакты на плате:
После перебора контактов JTAG с помощью JTagulator мне удалось подключиться, используя OpenOCD.
Другой способ восстановления системы реализуется через флеш-память, а именно микросхему Macronix NAND:
И здесь я задумался. У меня ведь есть рабочее устройство, на котором я вполне могу запустить загрузчик. А что, если при этом попробовать заменить его рабочий чип NAND на поврежденный, чтобы перепрошить?
Прежде, чем что-либо пробовать, я спросил коллегу, что он думает по поводу столь дурацкой идеи. Оптимистичных прогнозов он не дал, да и я, честно говоря, на них не особо рассчитывал. В итоге мы заключили небольшое пари по результату моего эксперимента, и я вернулся к работе.
Первым делом предстояло выяснить, переживет ли система удаление NAND в работающем состоянии? Я понимал, что для ответа на этот вопрос мне потребуется более методический подход, нежели просто «обдать работающее устройство горячим воздухом и извлечь микросхему». Для начала нужно было выяснить, как NAND запитана. Судя по схеме, Vcc подключены к микросхеме в следующих местах:
Определив дорожки подключения Vcc, проще всего ответить на основной вопрос можно было отключив их от NAND при работающей системе. Чтобы это сделать, изначально я попробовал отсечь эти дорожки и добавить проводные перемычки (рекомендую 36 AWG Magnet Wire), которые можно будет разъединить после запуска загрузчика:
С правой стороны я отсек трассу питания подальше, решив, что это место будет более удачным, поскольку от него запитывается несколько контактов NAND. При установке первой перемычки трассу я отсек небольшим ножом и зачистил покрытие абразивным карандашом:
Получилось так себе, потому что карандаш оказался великоват, и в итоге я оголил чересчур большой участок. Лучше использовать остроконечный нож, чтобы не натворить бардак и получить что-то подобное:
Припаяв и соединив провода, я запустил маршрутизатор и, дождавшись старта загрузчика (CFE), с помощью команды dn (dump nand) убедился в доступности NAND, затем обесточил микросхему, разъединив провода.
После отключения питания (отмечено как Vcc removed ), устройство перезагрузилось и не смогло запустить загрузчик, так как NAND была недоступна. Проблема оказалась в том, что точка отсечения питания справа отключала его подачу не только на NAND, но и на SoC. Чтобы не усложнять, я просто восстановил этот отрезок и проделал всю ту же процедуру с установкой перемычки в точке ближе к NAND:
Вернув систему в строй и повторив предыдущий тест, я получил ответ на изначальный вопрос: когда питание отключается разъединением проводов перемычки, система продолжает работать, что подтверждается командой dn :
После того, как я убедился в возможности отключения NAND на работающей системе без влияния на загрузчик, очередным шагом было попробовать физически извлечь отключенную NAND из платы.
Прогрев микросхему горячим воздухом, я поочередно приподнял пинцетом сначала правую, а затем левую стороны:
В результате этого процесса система перезапустилась и провалила попытку войти в загрузчик:
Так как стороны микросхемы при ее отсоединении я поднимал поочередно, посматривая при этом в консоль, то стало очевидно, что перезагрузка произошла при подъеме именно левой стороны:
Наиболее вероятной причиной было изменение состояния контактов Read Enable (RE#) или Ready/Busy (R/B#). Чтобы это проверить, я добавил к обоим перемычки:
Тут NAND пришлось установить на место, чтобы вернуть систему в загрузчик. Затем я в очередной раз отключил ее питание, разъединив ведущие на Vcc провода, а дорожкиRE# и R/B# привязал к земле:
Затем я снова поочередно извлек правую-левую стороны микросхемы, поглядывая в консоль загрузчика:
На этот раз он остался активен, и система в перезагрузку не ушла. Закончив очередной этап головоломки, я перешел к следующему – установке поврежденной NAND в работающее устройство.
Для припаивания NAND снова использовался горячий воздух. Первая попытка оказалась безуспешной, так как некоторые контакты замкнуло при моей попытке выровнять чип по обеим сторонам. На этом этапе ввиду сбоя в очередной раз пришлось ставить обратно рабочую NAND.
Во второй попытке я уже использовал небольшой листок бумаги для изолирования одной стороны NAND на время выравнивания и закрепления другой:
После установки первой стороны бумажку я убрал и припаял вторую. Загрузчик остался активен. Следующим шагом нужно было восстановить контакты RE#, RB# удалением заземляющей перемычки, а также вновь соединить перемычку Vcc. После окончательного восстановления подключения я выполнил dn , чтобы убедиться в доступности NAND:
Тест чтения завершился успешно, и через веб-интерфейс загрузчика я прошил микросхему заводским образом:
Как видно из вывода, прошивка прошла успешно, и система загрузила ОС устройства.
Уверен, некоторые читатели спросят: «Почему просто не использовать для перепрошивки NAND специализированный программатор?» Это абсолютно уместный вопрос, и, быть может, так даже правильнее, чем заниматься всей этой чепухой.
И все же считаю, что здесь будет уместна цитата персонажа из к/ф «Парк Юрского периода», которого играл Джефф Голдблюм:
Ваши ученые настолько озабочены вопросом о том, могут ли они что-то сделать, что забывают приостановиться и подумать, а надо ли вообще это делать.
Источник