Как графический формат прошлого века привёл к zero-click exploit в iOS

в 6:25, , рубрики: iMessage, iOS, nso group, Pegasus, zero-click, Блог компании М.Видео-Эльдорадо, взлом iphone, информационная безопасность, М.Видео, мессенджеры, разработка под iOS, реверс-инжиниринг, эксплойты, Эльдорадо
Как графический формат прошлого века привёл к zero-click exploit в iOS - 1

Тема информационной безопасности в сфере интересов команды М.Видео и Эльдорадо, поэтому делимся с вами очередным интересным переводным тематическим материалом. В начале 2022 года Citizen Lab удалось выявить zero-click-эксплойт на основе NSO iMessage, использовавшийся для атаки на активиста из Саудовской Аравии.

В этой серии из двух постов мы впервые расскажем, как работает zero-click-эксплойт iMessage.

Судя по нашим исследованиям и открытиям, это один из самых технически сложных эксплойтов, которые нам встречались, ещё больше демонстрирующий, что предоставляемые NSO возможности считались доступными только нескольким государствам.

Рассматриваемая в посте уязвимость была устранена 13 сентября 2021 года в iOS 14.8 в рамках CVE-2021-30860.

NSO

NSO Group — один из самых известных поставщиков «access-as-a-service», продающий пакеты решений для хакинга, которые позволяют представителям государств, не имеющим собственных отделов киберзащиты и кибератак, получить такие возможности, что сильно расширяет количество способных на такое стран.

Многие годы группы наподобие Citizen Lab и Amnesty International отслеживали использование мобильного шпионского пакета NSO под названием Pegasus. Несмотря на заявления NSO о том, что компания «оценивает потенциал нарушения прав человека, связанный с неправомерным использованием продуктов NSO», Pegasus связывают со взломом журналиста New York Times Бена Хаббарда саудовским режимом, взломом устройств защитников прав человека в Марокко и Бахрейне, атаками на сотрудников Amnesty International и с десятками других случаев.

В ноябре 2021 года США добавили NSO в «Список организаций», сильно ограничив компаниям США возможность заниматься бизнесом с NSO, и заявили в пресс-релизе, что «инструменты NSO позволили иностранным государствам реализовывать международные репрессии, что является практикой авторитарных правительств, преследующих диссидентов, журналистов и активистов за пределами своих границ для подавления оппозиции».

Citizen Lab удалось восстановить эти эксплойты Pegasus с iPhone, поэтому в этом анализе раскрываются возможности NSO против iPhone. Мы знаем, что NSO продаёт похожие zero-click-пакеты для атак на устройства Android; у Project Zero нет образцов таких эксплойтов, но если у вас они есть, то свяжитесь с нами.

С одного до нуля

В предыдущих случаях, например, в случае 2016 года с «диссидентом на миллион долларов», целям отправлялись ссылки в SMS-сообщениях:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 2

Скриншоты фишинговых SMS, переданные Citizen Lab в 2016 году. Источник.

Цель была взломана только после нажатия на ссылку, эта техника называется «one-click-эксплойт» («эксплойт в одно нажатие»). Однако в последнее время было задокументировано, что NSO предлагает своим клиентам технологию эксплойтов без нажатий (zero-click), при которых даже технически продвинутые цели, не нажавшие бы на фишинговую ссылку, совершенно не знают о том, что на них совершается атака. В ситуации zero-click взаимодействия пользователя не требуется. То есть атакующему не нужно отправлять фишинговые сообщения; эксплойт просто втихомолку работает в фоновом режиме. Кроме неиспользования устройства нет никаких других способов предотвращения zero-click-эксплойта; это оружие, от которого нет защиты.

Один странный трюк

Изначальной точкой входа для Pegasus на iPhone является приложение iMessage. Это значит, что атаку на жертву можно выполнить, просто зная его телефонный номер или имя пользователя AppleID.

iMessage имеет нативную поддержку изображений GIF, обычно небольших и низкокачественных анимированных картинок, популярных в культуре мемов. GIF можно получать и отправлять в чатах iMessage и они отображаются в окне чата. Apple хотела, чтобы эти GIF были бесконечно зациклены, а не проигрывались всего один раз, поэтому на очень ранних этапах конвейера парсинга и обработки iMessage (после получения сообщения, но задолго до его отображения), iMessage вызывает следующий метод в процессе IMTranscoderAgent (за пределами песочницы «BlastDoor»), выполняя парсинг любого полученного файла изображения с расширением .gif:

[IMGIFUtils copyGifFromPath:toDestinationPath:error]

Судя по имени селектора, вероятно, этот код предназначался для простого копирования файла GIF до редактирования поля количества циклов, но семантика метода другая. Внутри он использует CoreGraphics API для рендеринга исходного изображения в новый файл GIF по пути назначения. А то, что имя исходного файла должно заканчиваться на .gif, не означает, что это на самом деле файл GIF.

Библиотека ImageIO, как подробно рассказывалось в предыдущем посте Project Zero, используется для угадывания истинного формата исходного файла и его парсинга с полным игнорированием расширения файла. Благодаря этому трюку с «фальшивым gif» более 20 кодеков изображений внезапно становятся частью поверхности zero-click-атак на iMessage, и в их числе очень запутанные и сложные форматы, удалённо раскрывающие сотни тысяч строк кода.

Примечание: компания Apple проинформировала нас, что начиная с iOS 14.8.1 (26 октября 2021 года) ограничила количество форматов ImageIO, доступных из IMTranscoderAgent, и полностью устранила путь исполнения кода GIF из IMTranscoderAgent, начиная с iOS 15.0 (20 сентября 2021 года), и теперь декодирование GIF полностью выполняется в BlastDoor.

PDF в вашем GIF

NSO использует трюк с «фальшивым gif» для атаки на уязвимость в парсере CoreGraphics PDF.

PDF был популярной мишенью для атак около десятка лет назад из-за его многофункциональности и сложности. Кроме того, доступность Javascript внутри файлов PDF сильно упрощала разработку надёжных эксплойтов. Похоже, парсер CoreGraphics PDF не интерпретирует Javascript, однако NSO нашла в этом парсере нечто столь же мощное…

Сильное сжатие

В конце 1990-х каналы связи и ёмкость накопителей были гораздо дефицитнее, чем сегодня. Именно в таких условиях появился стандарт JBIG2. JBIG2 — это специализированный кодек изображений, предназначенный для сжатия изображений, пиксели в котором имеют только цвета в оттенках серого.

Он был разработан для обеспечения чрезвычайно высоких коэффициентов сжатия сканов текстовых документов, реализован и использовался в дорогих офисных сканерах и принтерах наподобие показанного ниже устройства XEROX WorkCenter. Если вы пользовались функцией scan to pdf подобных устройств около десяти лет назад, то с большой вероятностью ваш PDF содержит поток JBIG2.

Как графический формат прошлого века привёл к zero-click exploit в iOS - 3

Многофункциональный принтер Xerox WorkCentre 7500, в котором использовался JBIG2 из-за его функции scan-to-pdf. Источник..

Создаваемые такими сканерами файлы PDF были чрезвычайно малы, до нескольких килобайт. Для достижения такого чрезвычайно сильного сжатия JBIG2 использовал две новые технологии, которые важны для нашего эксплойта:

Техника 1: сегментация и подстановка

По сути, каждый текстовый документ, особенно написанный на языках с небольшими алфавитами наподобие английского и немецкого, состоит из множества повторяющихся букв (также называемых глифами). JBIG2 пытается сегментировать каждую страницу на глифы, а затем использует простое сопоставление паттернов для подбора похожих глифов:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 4

Простое сопоставление паттернов способно выявить все схожие формы на странице, в данном случае все «e».

На самом деле JBIG2 ничего не знает о глифах и не выполняет OCR (распознавание текста). Кодировщик JBIG просто ищет связанные области пикселей и группирует похожие области вместе. Алгоритм сжатия просто заключается в замене всех достаточно схожих областей копией только одной из них:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 5

Замена всех похожих глифов на копию одного из них позволяет получить достаточно хорошо читаемый документ и обеспечить очень высокий коэффициент сжатия.

В данном случае результат отлично читаем, но количество сохраняемой информации значительно уменьшено. Вместо хранения всей исходной информации о пикселях целой страницы достаточно сжатой версии «справочного глифа» для каждого символа и относительных координат всех мест, в которых будут созданы копии. Алгоритм распаковки работает с конечной страницей как с холстом и «рисует» одинаковый глиф во всех сохранённых местах.

Однако у такой схемы есть серьёзная проблема: плохой кодировщик может запросто заменить похоже выглядящие символы и это может иметь интересные последствия. В блоге Д. Крисела есть интересные примеры, в которых PDF отсканированных документов или отсканированных строительных чертежей содержат неверные величины. Это не те проблемы, которые важны для нас сейчас, но они стали одной из важнейших причин того, что JBIG2 перестал быть популярным форматом сжатия.

Техника 2: улучшение кодирования

Как говорилось выше, результат сжатия на основе замен имеет потери. После сжатия и распаковки отрисованный результат выглядит не так, как входящие данные. Однако JBIG2 поддерживает и сжатие без потерь, а также промежуточный режим сжатия с меньшим количеством потерь.

Это реализовано хранением (и сжатием) различий между подставляемым глифом и каждым исходным глифом. Вот пример маски различий между подстановленным символом слева и исходным символом без потерь посередине:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 6

Применение оператора XOR к битовым картам для вычисления разности изображений.

В этом примере кодировщик может сохранять показанную справа матрицу различий, а при распаковке маска различий подвергается XOR с подстановленным символом для восстановления исходных пикселей символа. Есть и другие трюки для дальнейшего сжатия этой маски при помощи промежуточных форм подстановленного символа, используемого как «контекст» для сжатия, но это не относится к теме нашего поста.

Вместо полного кодирования всех различий за один шаг этот процесс можно выполнять в несколько этапов и на каждой итерации используется логический оператор (AND, OR, XOR или XNOR) для задания, сброса и смены состояния битов. Каждый последующий этап улучшения приближает отрендеренный результат к оригиналу и это позволяет управлять уровнем потерь сжатия. Реализация этих этапов улучшения кодирования очень гибка и она может «считывать» значения, уже присутствующие на холсте результата.

Поток JBIG2

Основная часть декодера CoreGraphics PDF, похоже, является проприетарным кодом Apple, однако реализация JBIG2 взята у Xpdf, исходный код которого доступен свободно.

Формат JBIG2 — это последовательность сегментов, которую можно рассматривать как набор команд отрисовки, последовательно выполняемых за один проход. Парсер CoreGraphics JBIG2 поддерживает 19 типов сегментов, среди которых такие операции, как задание новой страницы, декодирование таблицы Хаффмана или рендеринг битовой карты в указанных координатах страницы.

Сегменты представлены классом JBIG2Segment и его подклассами JBIG2Bitmap и JBIG2SymbolDict.

JBIG2Bitmap представляет собой прямоугольный массив пикселей. Его поле data указывает на опорный буфер, содержащий холст рендеринга.

JBIG2SymbolDict группирует вместе JBIG2Bitmap. Целевая страница представлена как JBIG2Bitmap, то есть как отдельные глифы.

На JBIG2Segment можно ссылаться по номеру сегмента, а векторный тип GList хранит указатели на все JBIG2Segment. Для поиска сегмента по номеру сегмента последовательно сканируется GList.

Уязвимость

Уязвимость — это классическое переполнение целочисленной переменной при сопоставлении сегментов:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 7

numSyms это 32-битное целочисленное значение, объявляемое в строке (1). Передавая тщательно подготовленные справочные сегменты, можно многократным сложением в (2) вызвать переполнение numSyms до меньшего контролируемого значения.

Это меньшее значение используется в качестве размера распределения кучи в (3), то есть syms указывает на буфер недостаточного размера.

Внутри самого вложенного цикла в строке (4) значения указателей на JBIG2Bitmap записываются в буфер syms недостаточного размера.

Без ещё одного трюка этот цикл записал бы более 32 ГБ данных в буфер syms, что вызвало бы сбой. Чтобы избежать этого сбоя, куча обрабатывается таким образом, чтобы первые несколько операций записи от конца syms повреждали опорный буфер GList. Этот GList хранит все известные фрагменты и используется процедурой findSegments для отображения из номеров сегментов, переданных в refSegs, на указатели JBIG2Segment. Из-за переполнения указатели на JBIG2Segment в GList перезаписываются указателями на JBIG2Bitmap в (4).

Поскольку JBIG2Bitmap наследует от JBIG2Segment, виртуальный вызов seg->getType() успешно выполняется даже на устройствах, где включена Pointer Authentication (используемая для выполнения проверки слабых типов в виртуальных вызовах), но возвращаемый тип теперь не будет равен jbig2SegSymbolDict, из-за чего дальнейшие операции записи в строке (4) не достигаются и ограничиваются пределы повреждения памяти.

Как графический формат прошлого века привёл к zero-click exploit в iOS - 8

Упрощённая структура памяти при возникновении переполнения кучи; показан буфер недостаточного размера с опорным буфером GList и JBIG2Bitmap.

Безграничная свобода

Сразу после повреждённых сегментов GList атакующий подготавливает объект JBIG2Bitmap, представляющий текущую страницу (место, в которое выполняют рендеринг текущие команды отрисовки).

JBIG2Bitmap — это простые обёртки вокруг опорного буфера, хранящие ширину и высоту буфера (в битах), а также значение строки, определяющее, сколько байтов хранится для каждой строки.

Как графический формат прошлого века привёл к zero-click exploit в iOS - 9

Схема памяти объекта JBIG2Bitmap, в которой показаны поля номера сегмента (segnum), ширины (w), высоты (h) и строки, повреждённые в процессе переполнения.

Тщательным структурированием refSegs атакующий может остановить переполнение ровно через ещё три указателя на JBIG2Bitmap после конца буфера segments списка GList. Это перезаписывает указатель vtable и первые четыре поля JBIG2Bitmap, представляющие текущую страницу. Из-за того, как устроена схема пространства адресов iOS, эти указатели с большой вероятностью окажутся во вторых 4 ГБ виртуальной памяти, имеющих адреса от 0x100000000 до 0x1ffffffff. Так как всё оборудование iOS является little endian (то есть поля w и line скорее всего будут перезаписаны на 0x1 (самой старшей половиной указателя на JBIG2Bitmap), а поля segNum и h скорее всего будут перезаписаны самой младшей половиной этого указателя — достаточно случайным значением, зависящим от структуры кучи и ASLR примерно между 0x100000 и 0xffffffff.

Это присвоит текущей целевой странице JBIG2Bitmap неизвестное, но очень большое значение h. Так как это значение h используется для проверки границ и должно отражать распределённый размер опорного буфера страницы, это приведёт к снятию ограничений на холст для рисования. Это значит, что последующие команды сегмента JBIG2 смогут считывать и записывать в память за пределами исходных границ опорного буфера страницы.

Подготовка кучи помещает опорный буфер текущей страницы прямо под буфер syms недостаточного размера таким образом, что когда JBIG2Bitmap выйдет за свои границы, он сможет считывать и записывать свои собственные поля:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 10

Структура памяти, показывающая, как вышедший за границы опорный буфер может ссылаться на объект JBIG2Bitmap и изменять его поля, поскольку он расположен в памяти после опорного буфера.

При помощи рендеринга 4-байтных битовых карт в нужных координатах холста он может выполнить запись во все поля страницы JBIG2Bitmap, а благодаря тщательному выбору новых значений для w, h и line он может выполнять запись по произвольным смещениям от опорного буфера страницы.

На этом этапе становится возможной и запись в произвольные абсолютные адреса памяти, если вы знаете их смещения от опорного буфера страницы. Но как вычислить эти адреса? До этого момента эксплойт выполнялся в стиле, очень напоминающем «канонический» эксплойт скриптового языка, который в Javascript может завершиться неограниченным объектом ArrayBuffer с доступом к памяти. Но в таких случаях атакующий может запускать произвольный код на Javascript, который, очевидно, можно использовать для расчёта смещений и выполнения произвольных вычислений. Как это реализовать в парсере изображений, работающем за один проход?

Мой формат сжатия Тьюринг-полный!

Как говорилось выше, последовательность этапов реализации улучшения JBIG2 очень гибка. Этапы улучшения могут ссылаться и на битовую карту результата, и на ранее созданные сегменты, а также на результат рендеринга текущей страницы или сегмента. Тщательно подготовив контекстозависимую часть улучшения распаковки, можно создавать последовательности сегментов, в которых действия выполняют только операторы сочетания улучшений.

На практике это означает, что можно применять логические операторы AND, OR, XOR и XNOR между областями памяти по произвольным смещениям от опорного буфера текущей страницы JBIG2Bitmap. А поскольку он неограничен, можно выполнять эти логические операции с памятью по произвольным смещениям вне границ:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 11

Структура памяти, показывающая, как логические операторы можно применять за границами.

Всё становится по-настоящему интересным, когда мы доведём ситуацию до крайней точки. Что если вместо работы с подпрямоугольниками размером с глиф мы будем обрабатывать отдельные биты?

Теперь можно передать на вход последовательность команд сегментов JBIG2, реализующую последовательность логических операций с битами, применяемую к странице. И поскольку буфер страницы вышел за границы, эти битовые операции могут работать с произвольной памятью.

Благодаря простейшей проверке можно убедиться, что при помощи имеющихся операторов AND, OR, XOR и XNOR можно вычислять любую вычисляемую функцию — простейшее доказательство заключается в том, что можно создать логический оператор NOT, выполнив XOR с 1, а затем поместив перед ним вентиль AND, чтобы получить вентиль NAND:

Как графический формат прошлого века привёл к zero-click exploit в iOS - 12

Вентиль AND, соединённый с одним входом вентиля XOR. Другой вход вентиля XOR соединён с постоянным значением 1, образуя NAND.

Вентиль NAND является примером универсального логического вентиля; из него можно построить все остальные вентили и собрать цепь, способную вычислять любую вычисляемую функцию.

Цепи на практике

JBIG2 не имеет функций скриптинга, но в сочетании с уязвимостью он имеет возможность эмулировать цепи произвольных логических вентилей, работающих с произвольной памятью. Так почему бы просто не использовать его для создания собственной компьютерной архитектуры и выполнять скриптинг уже в ней? Именно это и делает эксплойт. При помощи более 70 тысяч команд сегментов, определяющих логические операции с битами, он определяет небольшую компьютерную архитектуру с такими особенностями, как регистры и полный 64-битный сумматор и блок сравнения, которые эксплойт использует для поиска в памяти и выполнения арифметических операций. Он не так быстр, как Javascript, но фундаментально является его вычислительным эквивалентом.

Разработчиками эксплойта написаны операции бутстреппинга для побега эксплойта из песочницы, выполняемые в этой логической цепи, и вся эта система работает в такой странной эмулируемой среде, созданной из одного прохода распаковки потока JBIG2. Это невероятно и одновременно пугающе.

Во втором посте мы конкретно рассмотрим, как эксплойту удаётся сбежать из песочницы IMTranscoderAgent.

Автор: М.Видео-Эльдорадо

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js