Как известно, выполняемый в анклаве код серьёзно ограничен в своей функциональности. Он не может делать системные вызовы. Он не может осуществлять операции ввода-вывода. Он не знает базового адреса сегмента кода хост-приложения. Он не может jmp'ить и call'ить код хост-приложения. Он не имеет представления о структуре адресного пространства, которой руководствуется хост-приложение (например, какие именно страницы промаппены или что за данные размещены на этих страницах). Он не может просить операционную систему промаппить ему кусок памяти хост-приложения (например, через /proc/pid/maps). Наивные попытки прочитать вслепую произвольную область памяти хост-приложения, – не говоря уже о попытках записи, – рано или поздно (скорее первое) приведут к принудительному завершению анклавной программы. Так происходит всякий раз, когда запрашиваемая анклавом область виртуального адресного пространства оказывается недоступной хост-приложению.
Сможет ли вирусописатель при таких суровых реалиях задействовать SGX-анклавы для реализации своих злонамеренных целей?
– Хак для зондирования адресов на предмет возможности их считывания
– Хак для зондирования адресов на предмет возможности записи
– Хак для перенаправления потока управления
– Что дают злодею три перечисленные выше хака
– Как злодей задействует эти хаки для создания ранзомвари
Исходя из всего перечисленного выше, принято считать, что анклав способен лишь на то, чтобы обслуживать хост-приложение, и что анклав не может проявлять собственную инициативу, в том числе злонамеренную. А значит, для вирусописателей анклавы не представляют практической ценности. Это поспешное предположение является одной из причин того, почему SGX-защита несимметрична: код хост-приложения не может получать доступ к анклавной памяти, тогда как анклавный код может читать и писать по любому адресу памяти хост-приложения.
Поэтому, случись так, что злонамеренному анклавному коду удастся делать произвольные системные вызовы от имени хост-приложения, выполнять от его имени произвольный код, сканировать память хост-приложения и находить в ней пригодные для злоупотребления ROP-цепочки, – он сможет захватить полный контроль над хост-приложением, в стелс-режиме. Сможет не только красть и шифровать пользовательские файлы, но и действовать от имени пользователя. Например, отправлять от его имени фишинговые письма или проводить DoS-атаки. Не страшась при этом даже самых современных защитных механизмов, таких как стековые канарейки и санитарная обработка адресов.
Мы покажем несколько хаков, посредством которых злодеи преодолевают вышеописанные ограничения, стремясь воспользоваться благами SGX в своих злонамеренных целях: в проведении ROP-атак. Либо для выполнения произвольного кода замаскированного под процесс хост-пиложения (аналогично process hollowing, который часто используется малварью), либо для маскировки уже готовой малвари (чтобы избавить своего зловреда от преследований со стороны антивирусов и других защитных механизмов).
Хак для зондирования адресов на предмет возможности их считывания
Поскольку анклав не знает, какие диапазоны виртуального адресного пространства доступны хост-приложению и поскольку при попытке чтения недоступного адреса анклав принудительно завершается, – перед злодеем встаёт задача найти способ отказоустойчивого сканирования адресного пространства. Найти способ составления карты доступных виртуальных адресов. Злодей решает эту задачу посредством нецелевого использования Intel’овской технологии TSX. Использует один из побочных эффектов TSX'а: если функцию доступа к памяти поместить в TSX-транзакцию, то исключения, возникающие из-за обращения к недопустимым адресам, подавляются TSX'ом, не доходя до операционной системы. При попытке доступа к недействительному адресу памяти, – прерывается только текущая транзакция, а не вся анклавная программа. Т.о. TSX позволяет анклаву безопасно получить доступ к любому адресу, изнутри транзакции, – без риска обрушения.
Если указанный адрес доступен хост-приложению, TSX-транзакция чаще всего завершается успехом. В редких случаях она может потерпеть неудачу из-за внешних воздействий, таких как прерывания (например, прерывания планировщика), вытеснение из кэша или одновременное изменение ячейки памяти несколькими процессами. В этих редких случаях TSX возвращает код ошибки, указывающий на то, что возникший сбой носит временный характер. В этих случаях нужно просто перезапустить транзакцию.
Если указанный адрес недоступен хост-приложению, TSX подавляет возникшее исключение (ОС не уведомляется) и отменяет транзакцию. Анклавному коду возвращается код ошибки, – чтобы он мог среагировать на факт отмены транзакции. Эти коды ошибок указывают на то, что рассматриваемый адрес недоступен хост-приложению.
У такого манипулирования TSX’ом изнутри анклава есть приятная для злодея особенность: поскольку на момент выполнения анклавного кода большинство аппаратных счётчиков производительности не обновляются, по ним невозможно отслеживать TSX-транзакции, выполняемые внутри анклава. Таким образом, злонамеренные махинации с TSX’ом остаются полностью невидимыми для операционной системы.
Кроме того, поскольку вышеописанный хак не полагается на какие-либо системные вызовы, его нельзя ни обнаружить, ни предотвратить, простым блокированием системных вызовов; что обычно даёт положительный результат при борьбе с «охотой на яйца».
Злодей применяет вышеописанный хак для поиска в коде хост-приложения гаджетов, пригодных для формирования ROP-цепочки. При этом ему не нужно зондировать каждый адрес. Достаточно прозондировать по одному адресу с каждой страницы виртуального адресного пространства. Зондирование всех 16 гигабайт памяти занимает около 45 минут (на Intel i7-6700K). В итоге злодей получает список исполняемых страниц, которые пригодны для конструирования ROP-цепочки.
Хак для зондирования адресов на предмет возможности записи
Для осуществления анклавного варианта ROP-атаки злодей нуждается в возможности искать доступные для записи неиспользуемые участки памяти хост-приложения. Злодей использует эти участки памяти для инжектирования поддельного стекового фрейма и для инжектирования полезной нагрузки (шеллкода). Суть в том, что злонамеренный анклав не способен требовать от хост-приложения выделить себе память, но вместо этого может использовать не по назначению уже выделенные хост-приложением участки памяти. Если конечно ему удастся найти такие участки, не обрушив анклав.
Злодей осуществляет этот поиск, эксплуатируя ещё один побочный эффект TSX'а. Сначала он, как и в предыдущем случае, зондирует адрес на предмет его существования, а затем проверяет, доступна ли для записи соответствующая этому адресу страница. Для этого злодей задействует следующий хак: помещает функцию записи в TSX-транзакцию, и после того как она выполнилась, но ещё до того как завершилась – принудительно обрывает транзакцию (explicit abort).
Глядя на код возврата из TSX-транзакции, злодей понимает, доступна ли она для записи. Если это «explicit abort», злодей понимает, что запись была бы успешной, если бы он довёл её до конца. Если же страница доступна только для чтения, то транзакция завершается ошибкой, – отличной от «explicit abort».
У такого манипулирования TSX’ом есть ещё одна особенность, приятная для злодея (помимо невозможности отслеживания через аппаратные счётчики производительности): поскольку все команды записи в память фиксируются только если транзакция прошла успешно, принудительное завершение транзакции гарантирует, что зондируемая ячейка памяти остаётся неизменной.
Хак для перенаправления потока управления
При осуществлении ROP-атаки из анклава, – в отличие от традиционных ROP-атак, – злодей может получить контроль над регистром RIP без эксплуатирования каких-либо багов в атакуемой программе (переполнение буфера или что-то в этом роде). Злодей может напрямую перезаписывать значение регистра RIP, хранящееся на стеке. В частности он может заменить значение этого регистра своей ROP-цепочкой.
Однако если ROP-цепочка длинная, то перезапись большого куска стека хост-приложения может привести к повреждению данных и к неожиданному поведению программы. Злодея, стремящегося проводить свою атаку скрытно, такое положение вещей не устраивает. Поэтому он создаёт себе поддельный временный кадр стека и хранит свою ROP-цепочку в нём. Поддельный кадр стека размещается в произвольном месте памяти, доступном для записи, – благодаря чему подлинный стек остаётся нетронутым.
Что дают злодею три перечисленные выше хака
(1) Сначала злонамеренный анклав посредством хака для зондирования адресов на предмет возможности их считывания, – ищет в хост-приложении ROP-гаджеты, пригодные для злоупотребления.
(2) Затем посредством хака для зондирования адресов на предмет возможности записи, – злонамеренный анклав идентифицирует в памяти хост-приложения участки, пригодные для инжектирования полезной нагрузки.
(3) Далее, анклав создаёт ROP-цепочку из гаджетов, обнаруженных на шаге (1), и инжектирует эту цепочку в стек хост-приложения.
(4) Наконец, когда хост-приложение натыкается на созданную в предыдущем шаге ROP-цепочку, начинается выполнение злонамеренной полезной нагрузки, – с привилегиями хост-приложения и с возможностью обращаться к системным вызовам.
Как злодей задействует эти хаки для создания ранзомвари
После того как хост-приложение передаёт анклаву управление через какой-нибудь из ECALL'ов (не подозревая, что этот анклав злонамеренный), злонамеренный анклав ищет в памяти хост-приложения свободное место для инжектирования кода (за свободные места принимает те последовательности ячеек, которые заполнены нулями). Затем посредством хака для зондирования адресов на предмет возможности их считывания, – анклав отыскивает в хост-приложении исполняемые страницы и генерирует ROP-цепочку, которая создаёт новый файл с именем «RANSOM» в текущей директории (при реальной атаке анклав шифрует существующие пользовательские файлы) и отображает сообщение с требованием выкупа. При этом, хост-приложение наивно полагает, что анклав просто складывает два числа. Как это выглядит в коде?
Для удобства восприятия введём через дефайны некоторую мнемонику:
Сохраняем исходные значения регистров RSP и RBP, чтобы после выполнения полезной нагрузки восстановить нормальную работу хост-приложения:
Ищем подходящий фрейм стека (см. код из раздела «хак для перенаправления потока управления»).
Находим подходящие ROP-гаджеты:
Находим место для инжектирования полезной нагрузки:
Строим ROP-цепочку:
Вот таким вот образом Intel'овская технология SGX, призванная противостоять зловредным программам, эксплуатируется злодеями для реализации противоположных целей.
Mnemonik
ru.wiktionary.org/wiki/нелицеприятный
«сможет ли вирусописатель реализовать свои справедливые цели»?