[iOS] How древний костыль от xPdf превратил iMessage в полигон для шпионского ПО. Разбор за 5 минут.

sashka123555

Участник
Автор темы
55
61
1793297778301.png

Сегодня я вам поведаю об очень интересной уязвимости, которая эксплуатировалось шпионским ПО Pegasus, стало скучно, почему бы и в оффтоп не написать что-то... В ней есть одна деталь, которая реализована невероятно здорово... У нее была очень красивая реализация. Для тех времен это было невероятно сладко. Там была уязвимость целочисленного переполнения.

Фреймворк для 2D-рендеринга в iOS.
Для 2D-рендеринга в iOS используется фреймворк CoreGraphics, Собственно внутри себя данная библиотека еще и тащит код открытого проекта xPdf, древнего опенсорсного парсера документов ,из которого инженеры Apple аккуратно вырезали синтаксический анализатор и парсер шрифтов. Вместо того чтобы по-человечески развивать PDF-движок или вкладываться в опенсорс, Apple просто форкнула старый Си-шный код начала нулевых, намертво зашила его в закрытый фреймворк. Главная уязвимость скрывалась именно в части этого самого xPdf, заключалась она, как я упомянул выше - в целочисленном переполнении -
Objective-C:
Guint numSyms; // (1)
 numSyms = 0;
 for (i = 0; i < nRefSegs; ++i) {
 if ((seg = findSegment(refSegs[i]))) {
 if (seg->getType() == jbig2SegSymbolDict) {
 numSyms += ((JBIG2SymbolDict *)seg)->getSize(); // (2)
 } else if (seg->getType() == jbig2SegCodeTable) {
 codeTables->append(seg);
 }
 } else {
 error(errSyntaxError, getPos(),
 "Invalid segment reference in JBIG2 text region");
 delete codeTables;
 return;
 }
 }
 ...
 // get the symbol bitmaps
 syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *)); // (3)
 kk = 0;
 for (i = 0; i < nRefSegs; ++i) {
 if ((seg = findSegment(refSegs[i]))) {
 if (seg->getType() == jbig2SegSymbolDict) {
 symbolDict = (JBIG2SymbolDict *)seg;
 for (k = 0; k < symbolDict->getSize(); ++k) {
 syms[kk++] = symbolDict->getBitmap(k); // (4)
 }
 }
 }
 }
Переменная numSyms объявлена как 32-битное целое ( пометка 1 ). Если мы будем раз за разом добавлять специально подготовленные ссылочные сегменты ( 2 ), в конце концов это приведет к переполнению numSyms до контролируемого небольшого значения. Но об этом ладно, теперь про базу, а потом к разбору самой уязвимости.

MAIN
В общем. В версии iOS 14 была уязвимость, а конкретно внутри метода [IMGIFUtils copyGifFromPath:toDestinationPath:error], который крутился в процессе IMTranscoderAgent ( в iMessage есть нативная поддержка GIF-анимации. Присланная в чат гифка воспроизводится в цикле бесконечно. Как только iMessage получает сообщение, еще до его вывода на экран вызывается метод в процессе ).

Собственно, в этом методе случился один косяк с логикой. По идее функция должна была просто взять и скопировать файл гифки с одного места в другое, но вместо обычного копирования байтов этот метод под капотом запускал полноценный рендеринг через определенные системные библиотеки. Система забивала на то, что у файла расширение .gif, и лезла проверять его реальный формат. А те, кто эксплуатировал это - подсовывали туда PDF, внутри которого был зашит поток JBIG2 - мега-древний кодек из 90-х для сжатия чёрно-белых факсов. Сам же транскодер занимается тем, что перекодирует файлы и оптимизирует их размер, чтобы айфон не умер от нехватки памяти, когда в чат отправляется пачка тяжелых картинок или видео. Т.е взять медиафайл , сжать его, подогнать под нужный формат экрана и сделать так, чтобы всё это красиво отображалось в интерфейсе.

Транскодер запускал парсер для этого мамонта, и там был баг целочисленного переполнения. Выше упомянул.

А теперь касаемо гениальной идеи. Сам же кодек JBIG2 умеет сжимать картинки без потерь, применять к пикселям логические операторы ( XOR, AND, XNOR ). Авторы атаки поступили по-настоящему имхо изысканно. Они отправили 70 000 мелких команд рендеринга. Каждая из них не рисовала картинку, а выполняла математическую операцию над битами в памяти. И из этих 70 тысяч логических вентилей они, прямо внутри парсера, собрали полноценный виртуальный ПК с собственными регистрами и 64-битным сумматором... А дальше уже выход из песочницы и загрузка основного модуля.

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

1793298147560.png

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

1793298177187.png

​
В таком случае текст по‑прежнему прекрасно читается, однако объем хранимой информации становится меньше. Вместо того чтобы хранить данные о пикселях всей страницы, для их отображения нужна только сжатая версия ссылочного глифа для каждого символа и относительные координаты мест ,где должны быть размещены его копии. При распаковке алгоритм расставляет глифы по местам, как бы рисуя ими на холсте... У этого метода есть свои недостатки, которые в результате и позволили проэксплуатировать уязвимость.

Все. Пока.