Повышение привилегий через Intel SYSRET

июня 15 2012

Команда Xen Security выложила в своем блоге довольно интересную статью о недавно обнаруженной уязвимости в реализации инструкции Intel SYSRET. Уязвимость получила номер CVE-2012-0217 и позволяет администратору гостевой системы повысить свои привилегии до уровня гипервизора.

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

Введение: канонические адреса, переключение контекста и SYSRET

Первое, что необходимо понять - это концепция канонических адресов. В ходе разработки 64 битного расширения набора инструкций x86, AMD решила не расширять виртуальное адресное пространство до полных 64 бит, а ограничиться 48ю битами (это сделано для того, чтобы с одной стороны иметь все преимущества большого адресного пространства, а с другой стороны - избежать потерь в производительности за счет увеличения уровней таблицы страниц (pagetable)).

AMD сделала так, чтобы процессор просто игнорировал дополнительные 16 бит адресного пространства, но в среде умных программистов появилась тенденция использования данных особенностей для проделывания трюков с сохранением информации в "неиспользуемых" областях адресного пространства. Тем не менее, эти хитроумные штуки будут сломаны, если когда либо процессоры решат дополнительно расширить адресное пространство и для предотвращения подобных игр было решено добавить ограничение на микропроцессорном уровне: любое 64 битное значение, которое может использоваться как виртуальный адрес должно иметь каноническую форму (canonical form), что означает, что биты 48-63 должны быть такими же как и бит под номером 47. Таким образом, адресное простнанство разбивается на два непересекающихся 128 терабайтных канонических куска:

0x0000000000000000 - 0x00007fffffffffff
0xffff800000000000 - 0xffffffffffffffff.

Каждый раз, когда 64 битное значение используется как адрес, оно проверяется на принадлежность к перечисленным диапазонам, и если результат проверки является отрицательным, процессор порождает исключение - #GP (general protection fault).

Следующая вещь, которую нужно осознать - процесс переключения контекста между гостевой операционной системой и гипервизором. Каждый раз в время перехода от низкого уровня привилегий к высокому уровню (как к выполнению кода ядра, так и гипервизора), низкопривелигированное содержимое регистров должно быть заменено на высокопривелигированное содержимое, при этом старое содержимое должно быть сохранено. Если во время выполнения гостевой операционной системы возникает исключение или прерывание, то RIP, указатель стека и селектор сегмента кода (в котором закодирован уровень привелегий) изменяются непосредственно процессором (то есть аппаратно), далее гипервизор сохраняет остаток состояния гостевой операционной системы в своем стеке, и обрабатывает прерывание.

На заре x86 системные вызовы обслуживались специальным прерываением или исключением: обычно гостевая операционная система вызывала INT прерывание для выполнения системного вызова или функции гипервизора и гипервизор должен был вызывать прерывание IRET для того, чтобы передать управление назад - гостевой операционной системе. Но при анализе производительности как командой AMD, так и командой Intel было выявлено, что процесс, который делает достаточно большое количество системных вызовов обычным способом (через исключения), работает значительно медленнее. Таким образом, обе команды независимо пришли к специальным инструкциям для произвольного повышения привилегий: в процессорах AMD это SYSCALL/SYSRET, в процессорах Intel - SYSENTER/SYSEXIT. Эти инструкции имеют слегка отличающуюся семантику и они доступны далеко не во всех режимах работы процессоров от обоих производителей. Так как SYSRET является частью стандарта x86-64 и доступна на всех 64 битных процессорах, то практически все 64 битные операционные системы и гипервизоры широко используют указанную инструкцию.

Версия реализации SYSRET от Интел, в отличие от АМД, имеет одну тонкость и эта тонкость как раз и является сутью всей проблемы.

SYSRET от Intel и AMD

Ключевая разница между SYSCALL/SYSRET и INT/IRET заключается в количестве сохраняемой при вызове информации, а также месте ее хранения. SYSCALL/SYSRET сохраняют и восстанавливают только значение RIP гостевой операционной системы, а также меняют селектор сегмента кода (что как раз таки и изменяет уровень привилегий). Более того, данные инструкции не производят чтения IDT и записиси его в фреймы стека, а производят копирование гостевой RIP в/из регистра RCX и селекторов сегментов и RIP гипервизора в/из других различных регистров процессора. Гипервизор же несет ответственность за сохранение и восстановление всех остальных регистров, включая указатель на вершину стека (RSP). В целом конечный эффект от выполнения инструкции SYSRET, на процессорах производства обоих фирм, заключается в:

- загрузке RIP из RCX
- изменении селектора сегмента кода на режим гостевой операционной системы

Теперь, как мы сказали ранее, RIP используется как виртуальный адрес, то есть его значение должно быть каноническим. Тем не менее, RCX является регистром общего назначения, и может хранить любое 64 битное число. Таким образом, если по каким либо причинам в RCX содержится неканоническое значение, при исполнении инструкции SYSRET процессор породит general protection fault (#GP).

Далее в игру вступает тонкая разница в реализации SYSRET. Спецификации от AMD и Intel включают в себя псевдокод, который является точным описанием работы каждой инструкции. В псевдокоде от AMD нет никакой прямой проверки на каноничность адреса, тем не менее, RIP изменяется после того, как уровень привелегий вернулся к назад гостевому уровню. Эксперимент подтвердил, что если вызвать SYSRET с неканоническим значением в RCX, то исключение #GP порождается в гостевом режиме. Но в псевдокоде от Intel присутствует прямая проверка каноничности адреса и она происходит до смены уровня привилегий. Это означает, что если SYSRET вызвана с неканоническим значением в RCX, то процессор от Intel породит исключение #GP в привелигированном режиме.

Эта тонкость значительна потому, что значение указателя на вершину стека, используемое для доставки #GP, меняется в зависимости от уровня привелегий. Если #GP доставляется в гостевом режиме, то процессор будет загружать RSP гипервизора из специальной, обозначенной для гипервизора, точки входа. Но если #GP доставляется из режима гипервизора, то процессор будет использовать текущий RSP, что может быть эффективной ловушкой для исключения.

Однако вспомним, что SYSRET не восстанавливает RSP - это работа гипервизора. Так, ко времени появления #GP, гипервизор уже восстановил гостевой RSP а именно тот, что был в гостевой системе во время вызова SYSCALL вместе с содержимым остальных регистров общего назначения.

Таким образом, если гость может заставить гипервизор иметь неканонический адрес, который надо поместить в RIP, то он может записать в RSP любое значение, которое он хочет поместить в память гипервизора и заставить процессор переписать ее, что может быть преобразовано в полноценный эксплоит.

The End

Так что вполне возможно, что в скором времени сможет появиться малварь, которая способна выбраться из гостевой операционной системы и заразить собой хост.

По материалам блога разработчиков Xen

0 коммент. »

Оставить комментарий