Какие проблемы безопасности возникают при отсутствии стека ядра? - PullRequest
2 голосов
/ 05 октября 2019

Какие проблемы безопасности могут возникнуть из-за того, что код ядра использует обычный стек процесса?

1 Ответ

4 голосов
/ 06 октября 2019

Тривиально для непривилегированного пользовательского пространства, чтобы сбить ядро, и довольно легко взять его на себя или "просто" получить root.

Управление в пространстве пользователя указателем стека ядра (используется асинхронно прерываниями) уничтожает любую возможность«непривилегированного» кода, предполагая, что указанный непривилегированный код является машинным кодом, контролируемым потенциальным злоумышленником. Или просто ошибки в пользовательском пространстве могут привести к сбою ядра.

Сбой ядра может быть простым, например xor esp,esp / int 0x80 или ожиданием прерывания по таймеру. Это, вероятно, приводит к сбою страницы из-за попытки переместить рамку исключения на несопоставленную страницу после того, как RSP переходит в 0xFF...8. (Ядро использует те же таблицы страниц, что и пользовательское пространство; в каждом PTE есть бит, который помечает его как «только ядро» или нет.) Ошибка при попытке доставить эту ошибку страницы приводит к ошибке другой страницы или GPF, иboom у вас тройная ошибка .

Управление RSP также позволяет тривиально перезаписывать произвольные адреса ядра с помощью рамки исключения, что потенциально влияет на то, что происходит на других ядрах.

Обратите внимание, что я использовал int 0x80 вместо syscall, потому что syscall переходит к точке входа, сохраненной в MSR без касающейся памяти (или изменения RSP). Теоретически ядро ​​может проверить, что оно действительно, прежде чем что-либо делать в этом случае. Но настоящие прерывания (включая программные прерывания) выдвигают CS: RIP и RFLAGS перед выполнением любых инструкций ядра. На реальных x86-64, прерывания используют значение ядра-RSP из TSS . Если этого не произойдет, пространство пользователя будет контролировать виртуальный адрес для этих хранилищ. (IDK, если даже можно настроить вещи для использования RSP пользовательского пространства без изменений, или если HW эффективно навязывает наличие стека ядра / стека ядра для каждой задачи.)

(Обычно точка входа ядра syscallиспользует swapgs и загрузку из gs:0 или что-то еще для загрузки указателя стека ядра из нижней части стека ядра.)


Принятие ядра (повышение локальной привилегии):

  • Запускать несколько потоков в одном процессе, чтобы они все использовали одно и то же виртуальное адресное пространство. (Или используйте разделяемую память POSIX или что-то еще и установите RSP там.)

  • Один поток хранит свой указатель стека на глобальном, где другие потоки могут его прочитать.

  • Этот поток делает системный вызов; ядро ​​использует свой стек для адресов возврата пространства данных и данных . Выберите один из них, например open() или stat(), для возврата функций ядра sys_open() или sys_stat() потребуется некоторое время, особенно если они блокируют дисковый ввод-вывод во время разрешения пути или доступа к индексу.

    Или еще проще nanosleep. (Системный вызов, который спит, оставляет состояние пользовательского пространства сохраненным в стеке ядра, где он в конечном итоге ret возвращается после переключения контекста обратно к этой задаче и возврата из вызова к schedule().) Блокировка дискового ввода-выводаэто излишне сложно. Хотя он действительно предоставляет много кода файловой системы в качестве возможных источников значений регистра;вы можете выбрать, какой адрес возврата вы хотите перезаписать.

  • Пока это происходит, другой поток пользовательского пространства изменяет эту память , получая контроль над ядром RIP / EIPи данные в стеке. Даже с неисполнимыми стеками ядра вы можете многое сделать. Читая адреса возврата, вы можете победить ASLR ядра, а затем узнать, как изменить их, чтобы перейти к любому коду ядра, который вы хотите.

Ядро использует ту же таблицу страниц, что и пользователь. пространство для чтения / записи / exec может быть установлено с помощью mprotect(PROT_EXEC) перед выполнением системного вызова. Исполняемые страницы стека сделали бы внедрение кода тривиальным. Но бит SMEP (Предотвращение выполнения в режиме супервизора), представленный в 2010 году , блокирует это, не разрешая выполнять вызов 0 exec страниц пользовательского пространства (бит U / S в записи таблицы страниц, который всегда будет установлен любымстраница «принадлежит» пользовательскому пространству). Еще один недавний пост в блоге .

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


Broadwell представил еще одну функцию, SMAP (Supervisor Mode Access Prevention) , которая защищает от этого . В активном состоянии ядро ​​выйдет из строя, если попытается даже прочитать страницу пользователя. Его нужно отключить около copy_to_user() и copy_from_user(), но достижение этих функций с RSP, указывающим на память пользовательского пространства, кажется маловероятным. call будет ошибкой при нажатии на обратный адрес. Возможно, в 32-разрядном ядре вы могли бы сделать системный вызов с ESP чуть выше 1: 3 разбиения пользователь / ядро, поэтому только некоторые вложенные вызовы функций перейдут из нижней части страницы ядра 1G в верхнюю пользовательскую страницу. Но если copy_to/from_user являются листовыми функциями (или не выполнять никаких вызовов функций, когда SMAP отключен), мы, вероятно, не сможем их атаковать.

Сбой ядра все равно будет тривиальным с SMAP, но это неDoS эксплуатирует сложнее. (Это цель настоящего x86-64: превращать возможные эксплойты в ошибки.) Тем не менее, в нашем гипотетическом x86 без стеков ядра, установка RSP на адрес ядра и системный вызов (без использования [RSP] в пространстве пользователя)позволит перезаписывать данные ядра по инструкциям ядра, что не останавливает SMAP. Смотрите ниже re: без многозадачности.


Или, если вы на самом деле не хотите запускать код в режиме ядра, вы можете просто ret кодировать код, который поднимает ваш процесс до уровня root, устанавливаяEUID = 0.

Вы можете управлять значениями в регистрах при достижении ret, выбирая, какой системный вызов вы делаете, и какие аргументы вы передаете. И какой уровень вызовов вложенных функций перезаписывает адрес возврата.


Обратите внимание, что системный вызов блокировки делает эту атаку возможной даже на одноядерном компьютере, где атакующий поток не может работать одновременнос кодом ядра. Он просто должен получить запланированное ядро, прежде чем системный вызов жертвы вернется, и вот что делает возможным блокирование.

В игрушечной системе без многозадачности (нет пути к ядру, которое когда-либо запускало какие-либо другие). код пользовательского пространства перед тем местом, которое он оставил), «все», что вы могли сделать , - это перезаписать произвольные адреса ядра адресов стековыми кадрами. Включая недопустимые системные вызовы (например, значение RAX, которое возвращает -ENOSYS в Linux) сброс содержимого регистров пользовательского пространства по известному шаблону и последующий возврат без особых помех для гораздо большего пространства стека !!! Предполагая, что точка входа syscall написана примерно так же, как в Linux, она проверяет номер вызова довольно рано, без множества вызовов / повторов, которые могут испачкать мусор там, где вы, возможно, не захотите этого, если захотите вступить во владение вместо простого сбоя.

Как только ваш неверный syscall вернется, вы восстанавливаете RSP до нормального значения, а затем делаете системный вызов, который использует любые данные, которые вы только что перезаписали, например, позволяя системному вызову завершиться успешно, что обычно не происходит. например, chmod + chown, чтобы сделать исполняемый файл SUID-корнем, или, если вам удалось установить текущие UID задач на ноль, запустите новую оболочку.

...