И снова здравствуйте! Уже завтра у нас стартуют занятия в новой группе по курсу «Реверс-инжиниринг». Традиционно делимся с вами переводом полезного материала по теме. Поехали!
Некоторым злоумышленникам важно, чтобы эксплойт был чрезвычайно надежным. Он всегда должен приводить к исполнению кода при запуске в системе с известной платформой и версией Flash. Чтобы создать его, можно использовать особо высококачественные баги. В этой статье описано использование одного из таких багов, а также факторы, которые делают его особенно подходящим для надежной эксплуатации.
Баг
CVE-2015-3077 — проблема путаницы типов в сеттерах фильтров Adobe Flash Button и MovieClip, которая позволяет спутать любой тип фильтра с любым другим. Я сообщила о нем в начале декабря 2015 года, и в мае его пофиксили. Баг происходит из-за того, что взломщик может перезаписать конструктор, используемый для инициализации объекта фильтра. Пример кода, воспроизводящего проблему, представлен ниже:
Этот код в какой-то степени путающий из-за использования оператора [], необходимого для компиляции в Flash CS. Логически эквивалентный код (который не факт, что скомпилируется) приведен ниже:
Этот код устанавливает поле фильтров объекта: Button или MovieClip на BlurFilter, которое затем хранится непосредственно в Flash. Конструктор BlurFilter затем перезаписывается конструктором ConvolutionFilter. После этого, вызывается геттер и создается объект ActionScript для хранения оригинального BlurFilter. Однако, конструктор уже перезаписан, поэтому вызывается ConvolutionFilter. Так получается объект типа ConvolutionFilter, поддерживаемый возвращением оригинального BlueFilter.
В конечном счете, к полям ConvolutionFilter можно получить доступ (на чтение и запись), как если бы они принадлежали BlurFilter. Аналогично для любого другого типа фильтра. Так открывается широкий спектр манипуляций, полезных для эксплойтинга.
На диаграмме ниже показано расположение оригинальных объектов в памяти, которые потенциально можно запутать, используя эту уязвимость в 64-битном Linux.
В двух случаях, указатели сопоставимы с целыми числами, и числами с плавающей точкой, которыми можно манипулировать. Это значит, что указатели можно читать и записывать напрямую. Кроме того, поскольку поля объектов упорядочены и отсортированы по размеру в соответствии с определением класса, они всегда находятся в предсказуемых местах, благодаря чему запись и чтение не дают сбоев. Эти свойства важны для обеспечения надежности эксплойта.
Эксплойт
Поскольку эксплуатация этой проблемы требует многократного запуска путаницы типов, я начала с создания служебной функции для путаницы типов, FilterConfuse.confuse. Она также наводит порядок: возвращает конструкторы фильтра ActionScript обратно в нормальное состояние для многократного вызова уязвимой функции без влияния на поведение ActionScript вне самой функции.
Первым шагом, нужно было обойти ASLR, определив адрес таблицы виртуальных функций (кратко vtable). Идеальный для этого способ — путаница объекта с vtable с объектом, в котором есть элемент перекрывающий vtable, которым можно манипулировать. Но vtable всех фильтр-объектов имеют одинаковое смещение. Вместо этого, я использовала BitmapData объект в DisplacementMapFilter для определения адреса vtable.
Для определения места в памяти BitmapData объекта, я перепутала DisplacementMapFilter с BevelFilter. Это заставило указатель BitmapData, хранящийся в DisplacementMapFilter, выровняться со свойствами цвета BevelFilter(shadowColor, shadowAlpha, highlightColor и highlightAlpha). Эти свойства поддерживаются двумя 32-битными целыми числами (показаны как scolor и hcolor сверху и снизу), и свойства цвета получают доступ к 24 битам каждого целого числа, в то время как alpha-свойства получают доступ к верхним 8 битам. Если считать эти свойства и объединить их с помощью битовой арифметики, можно извлечь непосредственный адрес BitmapData объекта.
Затем, нужно прочитать vtable из верхней части BitmapData объекта. Для этого я использовала свойство matrix объекта ConvolutionFilter. Оно хранится в виде указателя на массив чисел с плавающей точкой, под которые выделяется память при установке свойства, а массив ActionScript, содержащий эти числа, возвращается при получении свойства. Установив указатель matrix на BitmapData объект, можно прочитать из памяти содержимое этого объекта в виде массива чисел с плавающей точкой.
Чтобы задать указатель, я перепутала объект ConvolutionFilter с объектом DisplacementMapFilter (не тем же самым DisplacementMapFilter, что использовался выше!) и задала в свойстве mapPoint местоположение BitmapData объекта выше. Свойство mapPoint — точка с целочисленными координатами x и y (p_x и p_y на рисунке ниже), которые соответствует указателю matrix в ConvolutionFilter, что позволило легко задать это значение. После этого стало возможным чтение vtable из BitmapData объекта с помощью матричного массива из объекта ConvolutionFilter (стоит отметить, что для этого объект нужно было спутать с DisplacementBitmapFilter, а затем спутать обратно с ConvolutionFilter).
На этом этапе становится сложнее сохранять надежность эксплойта из-за использования чисел с плавающей точкой. Значения vtable_low и vtable_high считываются из ConvolutionFilter матрицы в виде чисел с плавающей точкой, так как это является типом массива. Но, к сожалению, не каждое допустимое значение указателя является допустимым числом с плавающей точкой. Это значит, что чтение значения вернет NaN, или хуже — не совсем корректное числовое значение.
В идеале, чтобы решить эту проблему, нужно получить доступ к vtable_low и vtable_high через геттер, который интерпретирует их как целые числа, но такого нет, потому что элементы фильтра обычно float в силу их функциональности.
К счастью, виртуальная машина AS2 достаточно ленива в отношении интерпретации чисел с плавающей точкой — она преобразует значение во float, только когда над ним совершается операция в ActionScript. Оригинальные операции обычно не требуют интерпретации, кроме специальных, вроде arithmetic. Это значит, что при копировании числа с плавающей точкой из матричного массива в vtable_low или vtable_high, оно сохранит свое значение в памяти, даже при его невалидности для float, пока переменная в которую оно было скопировано не используется в ActionScript или для совершения арифметических операций в родном коде. Таким образом, если значение переменной мгновенно путается с другим типом, который поддерживает полный диапазон 32-битных значений, например int, оно гарантированно будет тем же самым, что и оригинальное значение в памяти матричного массива. Поэтому, чтобы избежать появления ненадежности в эксплойте, важно провести путаницу типов до манипуляции с float’ами в ActionScript.
Для этого я написала класс преобразования, FloatConverter, использующий путаницу типов в фильтрах для реализации integer-to-float и float-to-integer функций. Он путает свойство matrix ColorMatrixFilter (не путайте с матричным свойством ConvolutionFilter), которое представляет собой набор встроенных float’ов, с GlowFilter свойствами color и alpha, которые обращаются к разным байтам int’а.
Так можно реализовать надежное преобразование float’а в int, но, к сожалению, в обратную сторону это не работает надежно. Для доступа к массиву color в ColorMatrix в ActionScript, копируется весь массив, даже если обращаешься только к первому из них. При копировании массива каждый элемент конвертируется в Number, что включает в себя обращение к указателям (например, вызов valueOf объекта). Поскольку массив color длиннее всего класса GlowFilter, он попадает в кучу при запутывании с GlowFilter. Это значит, что может произойти преобразование неизвестных значения из этой кучи, что приведет к крашу, если они ссылаются на недопустимые указатели при преобразовании в Number. Поэтому для int-to-float я реализовала float-конвертер, использующий другую путаницу ConvolutionFilter и DisplacementMapFilter, которая является прямым кастом и не вызывает неизвестные значения из кучи.
Это решает проблему крашей, вызванных обращением к незнакомым значениям из кучи, но, к сожалению, есть еще одна проблема надежности, связанная в этом эксплойте с float’ами. Она происходит из-за реализации матричного геттера ConvolutionFilter. Все числовые значения в ActionScript 2 имеют тип Number, который представляет собой объединение целого числа и указателя в число двойной точности (double). Оригинальная матрица ConvolutionFilter хранится в виде массива чисел с плавающей точкой, но копируется в массив ActionScript для сохранения доступа при вызове геттера матрицы, а значения в процессе преобразуются в double. Затем, при вызове float-конвертера, они преобразуются обратно в числа с плавающей точкой.
Приведение числа с плавающей точкой к числу двойной точности и обратно обычно сохраняет его значение, но не в случае, если float значение равно SNaN. Согласно спецификации плавающей точки, существует два типа NaN’ов: тихий NaN (QNaN) и сигнальный NaN (SNaN). При появлении QNaN ничего не происходит, но SNaN в некоторых случаях выдает исключение с плавающей точкой. В x86 преобразование double во float всегда приводит к QNaN (даже если double получился из SNaN), чтобы избежать неожиданных исключений.
Поэтому, если нижние биты указателя являются SNaN, он будет сконвертирован в QNaN, а значит первый бит (первый бит мантиссы, бит 22) будет задан, когда не должен. Эту проблему можно избежать при чтении vtable — третий байт указателя, содержащий первый бит, можно прочитать без выравнивания для подтверждения настоящего значения. Так код выполнит чтение без выравниваний (прочитав vtable повторно с указателем Bitmap, увеличенным на один) и скорректирует значение int, если float окажется SNaN.
Используя float-конвертеры, описанные выше, адрес vtable можно преобразовать в целое число. Теперь необходимо добиться выполнения кода при помощи этого адреса. Простой способ переместить указатель инструкции — перезаписать vtable объекта (или указатель на объект, у которого есть vtable). Это можно сделать с помощью путаницы матричного массива ConvolutionFilter и BitmapData указателя DisplacementFilter.
Конец первой части. Вторая часть перевода будет опубликована немного позже, а сейчас ждем ваши комментарии и приглашаем всех желающих на курс «Реверс-инжиниринг» от OTUS.
Автор: MaxRokatansky