Обзор эксплойтовКрис Касперски Хакер, номер #115, стр. 115-040-1 Этот обзор еще более необычен, чем предыдущий. Посвящен он ошибкам процессоров, обнаруженных за последнюю пару месяцев. Ведь даже если операционная система не содержит дыр (например, OpenBSD), удаленная атака все равно остается возможной – благодаря дефектам проектирования процессора!Intel Pentium – зависон на невыровненных циклахBriefВ июне 2008 года сотрудники тестовой лаборатории корпорации Intel обнаружили ошибку в процессоре Core 2 Extreme Processor QX9775, связанную с некорректной обработкой невыровненных условных переходов, пересекающих 16-байтовые границы. ЦП высаживался на измену, результаты которой варьируются от генерации исключения «machine check exception» до полного зависания системы. Баг возникает только на определенных временных диаграммах, а потому чаще всего он проявляется именно в компактных вложенных циклах (short nested loops), для выполнения которых достаточно запустить на машине жертвы специальный JavaScipt. Тогда (при активном Java-компиляторе, компилирующем код в память) мы получим тотальный отказ в обслуживании – то бишь DoS. Назвать эту ошибку новой нельзя. Впервые она была обнаружена в феврале 2008-го в процессоре Core 2 Extreme Processor QX9000, а спустя неделю — еще в куче других. Пока Intel рассылала разработчикам BIOS'ов рекомендации по устранению обозначенного дефекта, в руки тестеров попал новый (едва ли не новейший, а потому – жутко дорогой) Core 2 Extreme Processor QX9775, содержащий ту же самую ошибку, полученную по «наследству». TargetДефект выявлен в следующих процессорах: Dual-Core Xeon E3110, Quad-Core Xeon 3300, Dual-Core Xeon 5200, Quad-Core Xeon 5400, Core Extreme QX9775, Core 2 Extreme Quad-Core QX6000, Core 2 Quad Q6000, Core 2 Extreme QX9000, Core 2 Quad Q9000. Однако ошибка может присутствовать и в процессорах младше, уже снятых с производства, а потому и не тестируемых. ExploitИсходный текст exploit'а, написанный мной на ассемблерных вставках на MS Visual, C++ ищи на DVD. При выполнении на указанных процессорах с дряхлой версией BIOS'а наступает крах (зависон). Для переноса exploit'а на Java/JavaScript необходимо знать особенности Java-транслятора конкретного браузера, а потому готовые решения здесь не приводятся. SolutionОбновить прошивку BIOS (если производитель материнской платы исправил этот дефект процессора – что вовсе не факт!). Intel Pentium – убийство виртуальных машинBriefВ июне 2008 года в процессорах Core 2 Extreme Quad-Core QX6000 и Core 2 Quad Q6000 была обнаружена ошибка в менеджере виртуальных машин (Virtual Machine Manager или, сокращенно, VMM), позволяющая зловредному коду, исполняющемся на нулевом кольце, «убивать» текущую виртуальную машину. Линейку NT-подобных систем этим не удивишь, так как с нулевого кольца легко устроить BSOD даже безо всяких ошибок в ЦП. А вот Linux/BSD некорректным модулем ядра завалить труднее. К тому же, если виртуальная машина предусматривает автоматический рестарт гостевой оси при возникновении каких-то «терок» (что часто встречается на виртуальных серверах, работающих на «автопилоте»), – убийство VM превращается в реальную угрозу. Оказывается, достаточно задействовать опцию IA32_DEBUGCTL.FREEZE_WHILE_SMM_EN, которую можно активировать посредством записи установки бита FREEZE_WHILE_SMM_EN в MSR-регистре IA32_DEBUGCTL. И все! Виртуальная гостевая машина аварийно завершится кодом 80000021h. Хана, короче! Подробнее можно прочитать в разделе «VM-Entry Failures During or After Loading Guest State» руководства «Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide, Part 2». TargetВ настоящее время дефект обнаружен и подтвержден в кристаллах Core 2 Extreme Quad-Core QX6000 и Core 2 Quad Q6000. Про остальные процессоры пока ничего неизвестно, но вполне возможно, что они также содержат эту ошибку. ExploitИсходный текст exploit'а, написанный мной на ассемблере, приведен ниже (чтобы использовать, необходимо подставить фактический номер MSR регистра в его имя, воспользовавшись соответственным заголовочным файлом от Intel или сделать это своими руками — номера MSR и всех их атрибутов содержатся в документации). Убийство гостевой виртуальной машины четырьмя ассемблерными командами MOV ECX, IA32_DEBUGCTL RDMSR ; читаем содержимое IA32_DEBUGCTL в EDX:EAX OR EAX, 4000h ; взводим бит FREEZE_WHILE_SMM_EN WRMSR ; обновляем MSR-регистр SolutionIntel предлагает как решение на уровне BIOS'а, так и программные «костыли», требующие модификации кода виртуальной машины. Однако, ни разработчики VM, ни Microsoft (со своим Server'ом 2008, в состав которого входит виртуализатор) никак не отреагировали на ситуацию. Самое смешное, что даже сама Intel, выпустившая виртуализатор VirtualBox (бесплатный, кстати), не стала править код. Так что... защита нам только снится :). Intel Pentium – крах основной машиныBriefВ конце марта 2008 года в процессоре Dual-Core Xeon 7000 был обнаружен мелкий, но весьма противный дефект варварского типа, «поджидающий» кристалл в узких переходах между 64-битным режимом основной (host) операционной системы и 32-битным режимом гостевой виртуальной машины. При задействованном режиме Hyper-Threading процессор (при стечении определенных обстоятельств) с некоторой (впрочем, довольно незначительной) вероятностью либо выбрасывает IERR#, либо уходит в глухой зависон, отправляя в небытие не только виртуальные машины, но и основную операционную систему. А вот это уже нехорошо! Подробности этого увлекательного круиза смерти можно найти в официальном обновлении спецификаций «Specification Update»: download.intel.com/design/xeon/specupdt/309627.pdf. TargetОшибка в настоящее время обнаружена и подтверждена для Dual-Core Xeon 7000, но нельзя исключать, что ее нет и в других процессорах этого семейства. ExploitРуками не трогать! Оно и само упадет, со временем. А чтобы помочь упасть, достаточно выполнять больше переходов между 32-разрядной виртуальной гостевой машиной и 64-битной операционной системой. Как? Да очень просто — достаточно, например, создать шторм TCP/IP-пакетов, на физическом уровне обрабатываемый сетевой картой основной операционной системы, а, значит, переключающий контекст выполнения на ее драйвер (для обработки очередного прерывания). Пройдет немного времени и случится глобальный завис! SolutionПо утверждению Intel, проблему можно решить на уровне BIOS'а. Фирма разослала ведущим производителям BIOS'ов и материнских плат рекомендации по обходу бага, но что-то те не спешат реагировать. И обновленные прошивки BIOS'а этому дефекту процессора «совершенно перпендикулярны». А отдуваются, как всегда, конечные пользователи. Ошибка #IRET на службе руткитовBriefПоздравляем! Intel веников не вяжет! 14 мая 2008 года в новейшем кристалле Core 2 Extreme QX9000 обнаружен древний баг, известный еще с декабря 2005. Очень красивый, элегантный и чертовски полезный баг. Затрагивает множество процессоров, выпущенных корпорацией Intel за последние несколько лет. Это позволяет использовать его для защиты программного кода от всяких там дизассемблеров, эмулирующих отладчиков и реверсеров, отлаживающих малварь на живых, но слегка устаревших машинах. Но обо всем по порядку! Баг связан с инструкцией IRET, традиционно использующейся в обработчиках аппаратных прерываний. Однако команда может использоваться и на прикладном уровне для передачи управления на кольцо с идентичным уровнем привилегий. При этом IRET последовательно выталкивает из стека регистр EIP, селектор CS и содержимое флагов. Если предварительно сохранить в стеке флаги, текущий CS и указатель на метку label, то мы получим завуалированный аналог jmp label, но только jmp label распознает любой дизассемблер, а IRET – обламывает ИДУ и по самые помидоры. Создавать перекрестные ссылки приходится вручную. Впрочем, это — мелочи. Все самое интересное сидит внутри IRET. Только с виду она кажется простой командой. Даже в x86-процессорах псевдокод, поясняющий действие IRET и приводимый в мануалах от Intel, занимал несколько страниц (а реальный микрокод – и того больше). Поддержка 64-битного режима усложнила IRET в несколько раз. Во-первых, 64-битный режим требует обязательного выравнивания там, где в x86-процессорах оно было опционально. А теперь вопрос на засыпку: как должен вести себя процессор, если на момент начала выполнения IRET флаг AC сброшен (контроль выравнивания отключен), стек не выровнен, а в сохраненном значении регистра флагов (который будет извлечен после завершения инструкции IRET) флаг AC взведен? x86-процессоры (и некоторые x86-64) на это реагируют вполне адекватно, то есть врубают контроль выравнивания после того, как IRET закончит свою работу. Это вполне логично, поскольку с точки зрения программиста все инструкции неделимы и выполняются за одну абстрактную итерацию. Однако, поскольку мы имеем дело с RISC-ядром, выполняющим микрокод, то из-за ошибок в этом самом микрокоде, контроль выравнивания включается во время выполнения инструкции IRET, что приводит к генерации прерывания Alignment Check Exception (#AC). Причем, ошибке подвержена только та часть микрокода IRET, которая отвечает за передачу управления с кольца 3 на кольцо 3. Межкольцевой вызов в обозначенной ситуации исключения не вызывает. TargetДефект обнаружен и подтвержден в следующих процессорах (пристегни ремни безопасности, прежде чем читать): Dual-Core Xeon E3110, Dual-Core Xeon 3000, Quad-Core Xeon 3200, 64-bit Intel Xeon, Quad-Core Xeon 3300, Dual-Core Xeon 5000, Dual-Core Xeon 5100, Quad-Core Xeon 5300, 64-bit Xeon MP, Dual-Core Xeon 7000, Dual-Core Xeon 7100, Dual-Core Xeon 7200, Quad-Core Xeon 7300, Core 2 Extreme QX9775, Core 2 Extreme Quad-Core QX6000, Core 2 Quad Q6000, Core 2 Extreme QX9000, Core 2 Quad Q9000. Возможно, что и в некоторых других :). ExploitКак эту дыру можно использовать для атаки? А никак! Если программное обеспечение, используемое жертвой, применяет IRET для передачи управления с кольца 3 на кольцо 3 (а зачем ему это делать?), да еще работает с невыровненным стеком и выталкивает в регистр флагов установленный бит AC – оно само упадет. Intel даже наблюдала такое поведение на некоторых программах, правда, не сказала, каких. Но падает только «неправильное» приложение, а не вся система целиком и чтобы выполнить IRET на атакуемой машине, хакеру нужно иметь хотя бы минимальные права для выполнения своего кода (исполняемого файла или shell-кода). В таком случае уронить приложение можно и без всякого IRET'а. Смысл? А в том, что IRET передаст управление вовсе не туда, куда ожидалось! То есть, мы можем написать очень хитрый код, который в дизассемблере (или под эмулирующим отладчиком) выполняет невинные действия, а вот на живой машине (с багистным процессором) не только передает управление на основное тело, но и использует код исключения для его расшифровки. Если исследователь малвари не в курсе этого бага, ему придется изрядно попыхтеть. Исходный код ассемблерного кода, демонстрирующего эту уязвимость, приведен ниже: Ассемблерный текст exploit'а, генерирующего #AC исключение на багистных процессорах и не генерирующего на правильных DEC ESP ; делаем стек не выровненным PUSHFD ; сохраняем флаги в стеке POP EAX ; выталкиваем в EAX OR EAX, 40000h ; взводим AC бит PUSH EAX ; сохраняем EAX в стеке PUSH CS ; сохраняем селектор кода PUSH offset my_next ; сохраняем адрес перехода IRET my_next: ; сюда предполагается передать управление INC ESP ; возвращаем стек на место SolutionА что тут можно сделать? Конечным пользователям — просто расслабиться. Реверсерам малвари — учитывать эту фичу при анализе кода. Попытка практической реализации proof-of-concept exploit'а поначалу не предвещала никаких неожиданностей. Горизонт был чист, сияло солнце. А между тем, гроза уже висела над моей норой: #AC-исключение не генерировалось! Хоть убей! Чего только я не делал: курил, точил, долбил, перечитывал errata от Intel столько раз, что выучил наизусть. По конец даже засомневался в здравости рассудка (своего или инженеров из Intel). И как всегда, виноватой оказалась Microsoft. Анализ обработчика исключений, сосредоточенного в ядре, показал, что система наглым образом ныкает #AC-исключение, не передавая его на прикладной уровень, и потому – до SEH/VEH обработчиков оно просто не доходит. Как в анекдоте: «До Штирлица не дошло письмо Центра». Мои раскопки ядра подтвердились несколькими независимыми источниками. Так, например, Program Manager (не программа, а сотрудник группы Microsoft Visual C++) по имени Kang Su Gatlin без ложной скромности писал: «On the x86 architecture, the operating system does not make the alignment fault visible to the application» (msdn.microsoft.com/en-us/library/aa290049(VS.71).aspx). Испытал это на своей шкуре хакер Zahical, цитируя мистера Kang Su Gatlin на форуме, где тусуются сишные программисты, причем довольно хорошо и плотно так тусуются: tech-archive.net/Archive/VC/microsoft.public.vc.language/2004-12/0199.html. Уточним — x86-ядро «зажимает» #AC-исключение, а вот что касается x86-64 – все зависит от того, был ли запущен процесс с флагом SEM_NOALIGNMENTFAULTEXCEPT, переданным API-функции SetErrorMode(). И если да, то исключение вновь будет «зажиматься». К счастью, процесс по умолчанию стартует без этого флага, и приведенный выше исходный код exploit'а будет работать, как часы. Ладно, оставим x86-64 системы. С ними все слишком просто, да и не очень-то они широко распространены. Вернемся назад к x86 и посмотрим, что можно сделать. Итак, ядро зажевало исключение и нам его никак не получить, разве что спуститься на ядерный уровень. Стоп! А ведь это – шикарный способ передачи управления ядерному модулю! Допустим, у нас есть rootkit, устанавливающий драйвер, с которым взаимодействует прикладной код. Стандартный интерфейс DeviceIoControl() слишком известен и слишком заметен. А вот если драйвер перехватывает вектор #AC-исключения (что на ядерном уровне реализуется без проблем, например, путем прямого хука таблицы дескрипторов прерываний — она же IDT), то IRET с невыровненным стеком заставит процессор сгенерировать исключение, подхватываемое нашим драйвером. Можно до упаду анализировать малварь, но в упор не видеть, что IRET передает управление не с ring-3 на ring-3 (согласно документации), а на ring-0. И уже нашему драйверу решать, что делать дальше. Можно передать управление обратно на ring-3 по адресу, занесенному в стек, но ведь можно и не передавать! Или передавать, но не туда. Или (как уже говорилось выше) использовать вектор исключения для расшифровки остального тела rootkita. Конечно, ключ получается какой-то беспонтовый и не слишком криптостойкий, но здесь главное — скрыть сам факт расшифровки. Благодаря ошибке в ЦП, он очень даже хорошо скрывается. Конечно, код будет работать не на любом ЦП (хотя и на многих), но малварь (в отличие от коммерческих программ) это обстоятельство как-нибудь переживет. Не один компьютер будет заражен, так другой. Хорошо, а как быть, если у нас драйвера нет, а писать его в лом? Можно ли обнаружить, что исключение имело место быть? Конечно! Обработка исключений — далеко не самая дешевая операция (в плане процессорных тактов) и, замеряя время выполнения IRET, мы легко установим истинное положение дел. «Правильная» IRET занимает несколько сотен «тиков», легко измеряемых инструкцией RDTSC. А вот скрытая обработка исключения ядром уже тянет на десятки тысяч. Если операция измерения времени выполнения IRET несильно бросается в глаза, то хакер легко запутает реверсеров, анализирующих малварь. И уж точно обойдет всякие эмулирующие отладчики и многие виртуальные машины. Учитывая, что Intel фиксить эту багофичу не собирается, стоит взять ее на вооружение! Тем более – это не единственная дыра в процессорах. Есть и другие. Да там их сотни! Я как раз сейчас нарабатываю фактический материал и готовлю доклад, который (при благоприятном стечении обстоятельств) будет прочитан на хакерской конференции «Hack In The Box Security» в Малайзии в октябре 2008 года. Для желающих посетить мероприятие напоминаю, что Малайзия очень удобна тем, что не требует визы, достаточно одного паспорта: conference.hackinthebox.org. |