В этой статье подробно описывается эксплойт для CVE-2022-42703 (выпуск P0 2351 — исправлено 5 сентября 2022 г.), ошибки, которую Янн Хорн обнаружил в подсистеме управления памятью (MM) ядра Linux, которая приводит к использованию после освобождения в структуре anon_vma. Поскольку ошибка очень сложная (мне, конечно, трудно ее понять!), в будущем сообщении в блоге она будет описана полностью. На данный момент запись в системе отслеживания проблем, эта статья LWN, объясняющая, что такое anon_vma, и коммит, вызвавший ошибку, являются отличными источниками для получения дополнительного контекста.
Настройка
Успешное срабатывание базовой уязвимости приводит к тому , что folio->mapping указывает на освобожденный объект anon_vma. Затем вызов madvise(..., MADV_PAGEOUT) можно использовать для повторного запуска доступа к освобожденному anon_vma в folio_lock_anon_vma_read() :
Один из возможных приемов эксплойта — позволить функции вернуть указатель anon_vma и попытаться заставить последующие операции сделать что-то полезное. Вместо этого мы решили использовать вызов down_read_trylock() внутри функции для повреждения памяти по выбранному адресу, что мы можем сделать, если сможем управлять указателем root_anon_vma, который считывается из освобожденного anon_vma.
Управление указателем root_anon_vma означает восстановление освобожденного anon_vma с памятью, контролируемой злоумышленником. Структуры struct anon_vma выделяются из собственного кеша kmalloc, а это означает, что мы не можем просто освободить одну и восстановить ее с помощью другого объекта. Вместо этого мы возвращаем связанную slab-страницу anon_vma обратно в распределитель страниц ядра, следуя стратегии, очень похожей на описанную здесь - (https://googleprojectzero.blogspot.com/2021/10/how-simple-linux-kernel-memory.html). Освобождая все объекты anon_vma на slab-странице, а затем дампя частичный список свободных мест percpu slab-страницы, мы можем вызвать виртуальную память, ранее связанную с anon_vma для возврата обратно в распределитель страниц. Затем мы делаем распыление буферов канала, чтобы вернуть освобожденный anon_vma с памятью, контролируемой злоумышленником.
На данный момент мы обсудили, как превратить наше использование после освобождения в вызов down_read_trylock() для указателя, контролируемого злоумышленником. Реализация down_read_trylock() выглядит следующим образом:
Было полезно эмулировать down_read_trylock() в unicorn, чтобы определить, как он ведет себя при разных значениях sem->count. Предполагая, что этот код работает с инертной и неизменной памятью, он будет увеличивать sem->count на 0x100, если 3 младших бита и самый старший бит не установлены. Это означает, что трудно изменить указатель ядра, и мы не можем изменить какие-либо значения, не выровненные по 8 байтам (поскольку они будут иметь установленный один или несколько из трех нижних битов). Кроме того, этот семафор позже разблокируется, в результате чего любая запись, которую мы выполняем, будет отменена в ближайшем будущем. Кроме того, на данный момент у нас нет установленной стратегии для определения слайда KASLR или определения адресов любых объектов, которые мы могли бы захотеть перезаписать нашим новообретенным примитивом. Оказывается, независимо от какой-либо рандомизации, имеющейся в настоящее время в ядре, существует прямая стратегия использования этой ошибки даже при такой ограниченной произвольной записи.
Повреждение стека…
В x86-64 Linux, когда ЦП выполняет определенные прерывания и исключения, он переключится на соответствующий стек, который сопоставлен со статическим и неслучайным виртуальным адресом, с другим стеком для разных типов исключений. Краткую документацию по этим стекам и их родительской структуре cpu_entry_area можно найти здесь - https://docs.kernel.org/x86/pti.html. Эти стеки чаще всего используются при входе в ядро из пользовательской среды, но они также используются для исключений, которые происходят в режиме ядра. Недавно мы видели записи KCTF, в которых злоумышленники используют нерандомизированную область cpu_entry_area для доступа к данным по известному виртуальному адресу в памяти, доступной ядру, даже при наличии SMAP и KASLR. Вы также можете использовать эти стеки для подделки контролируемых злоумышленником данных по известному виртуальному адресу ядра. Это работает, потому что содержимое регистра общего назначения задачи злоумышленника помещается непосредственно в этот стек, когда происходит переключение из режима пользователя в режим ядра из-за одного из этих исключений. Это также происходит, когда само ядро генерирует исключение таблицы стека прерываний и переключается на стек исключений, за исключением того, что в этом случае вместо этого помещаются GPR ядра. Эти проталкиваемые регистры позже используются для восстановления состояния ядра после обработки исключения. В случае исключения, вызванного пользовательской средой, содержимое регистра восстанавливается из стека задач.
Одним из примеров исключения IST является исключение DB, которое может быть запущено злоумышленником через аппаратную точку останова, связанные регистры которой описаны здесь - https://pdos.csail.mit.edu/6.828/2004/readings/i386/s12_02.htm . Аппаратные точки останова могут запускаться различными типами доступа к памяти, а именно чтением, записью и выборкой инструкций. Эти аппаратные точки останова могут быть установлены с помощью ptrace(2) и сохраняются во время выполнения режима ядра в контексте задачи, например, во время системного вызова. Это означает, что аппаратная точка останова, установленная злоумышленником, может быть активирована в режиме ядра, например, во время копирования_в/из_пользовательского вызова. Результирующее исключение будет сохранять и восстанавливать контекст ядра с помощью вышеупомянутого нерандомизированного стека исключений, и этот контекст ядра является исключительно хорошей целью для нашего произвольного примитива записи.
Любой из регистров, которые copy_to/from_user активно использует во время обработки аппаратной точки останова, можно повредить, используя наш примитив произвольной записи для перезаписи их сохраненных значений в стеке исключений. В этом случае размер вызова copy_user является интуитивно понятной целью. Значение размера постоянно сохраняется в регистре rcx, который будет сохраняться по одному и тому же виртуальному адресу каждый раз, когда срабатывает аппаратная точка останова. После повреждения этого сохраненного регистра нашим примитивом произвольной записи ядро восстановит rcx из стека исключений, как только он вернется обратно в copy_to/from_user. Поскольку rcx определяет количество байтов, которое должен скопировать copy_user, это повреждение приведет к тому, что ядро незаконно скопирует слишком много байтов между пользовательской средой и ядром.
… порождение повреждения стека
Стратегия атаки начинается следующим образом:
1. Форкается процесс Y из процесса X.
2. Процесс X отслеживает процесс Y, затем устанавливает аппаратную точку останова по известному виртуальному адресу [addr] в процессе Y.
3. Процесс Y делает большое количество вызовов uname(2), который вызывает copy_to_user из буфера стека ядра в [addr]. Это приводит к тому, что ядро постоянно запускает аппаратную точку наблюдения и входит в обработчик исключений БД, используя стек исключений БД для сохранения и восстановления состояния copy_to_user.
4. Одновременно выполняется много произвольных операций записи в известное расположение сохраненного значения rcx стека исключений БД, которое является сохраненной длиной copy_to_user процесса Y.
Стек исключений БД используется редко, поэтому маловероятно, что мы испортим какое-либо неожиданное состояние ядра с помощью ложного исключения БД, рассылая спам нашим произвольным примитивом записи. Техника также колоритна, но пропустить гонку просто означает испортить устаревшие данные стека. В этом случае мы просто пробуем снова. По моему опыту, для успешной победы в гонке редко требуется больше нескольких секунд.
При успешном искажении значения длины ядро скопирует большую часть стека текущей задачи обратно в пространство пользователя, включая файл cookie локального стека задачи и адреса возврата. Впоследствии мы можем инвертировать нашу технику и вместо этого атаковать вызовом copy_from_user. Вместо того, чтобы копировать слишком много байтов из стека задач ядра в область пользователя, мы вынуждаем ядро копировать слишком много байтов из области пользователя в стек задач ядра! Мы снова используем системный вызов prctl(2), который выполняет операцию copy_from_user.call буфера стека ядра. Теперь, искажая значение длины, мы создаем в этой функции условие переполнения буфера стека, которого раньше не было. Поскольку мы уже слили файл cookie стека и слайд KASLR, очень просто обойти обе защиты и перезаписать адрес возврата.
Завершение цепочки ROP для ядра оставляем читателю в качестве упражнения.
Получение слайда KASLR с предварительной выборкой
Сообщив об этой ошибке группе безопасности ядра Linux, мы предложили начать рандомизировать расположение percpu cpu_entry_area (CEA) и, следовательно, связанных стеков исключений и системных вызовов. Это эффективное средство защиты от злоумышленников, но его недостаточно, чтобы предотвратить использование преимущества локальным злоумышленником. 6 лет назад Дэниел Грусс и соавт. обнаружил новый, более надежный метод использования побочного канала синхронизации TLB в процессорах x86. Их результаты показали, что инструкции предварительной выборки, выполняемые в пользовательском режиме, удаляются со статистически значимыми различными задержками в зависимости от того, был ли запрошенный виртуальный адрес для предварительной выборки сопоставлен или не сопоставлен, даже если этот виртуальный адрес был сопоставлен только в режиме ядра. https://docs.kernel.org/x86/pti.html - был полезен для защиты этого побочного канала, однако большинство современных процессоров теперь имеют встроенную защиту от Meltdown, для решения которой был специально разработан kPTI, и, таким образом, kPTI (который имеет значительные последствия для производительности) отключен на современных микроархитектурах. Это решение означает, что снова можно воспользоваться преимуществом побочного канала предварительной выборки, чтобы обойти не только KASLR, но и защиту рандомизации области входа ЦП, сохраняя жизнеспособность метода эксплойта повреждения стека CEA против современных ЦП X86.
На удивление мало быстрых и надежных примеров этой техники обхода KASLR с предварительной выборкой, доступных в области открытого исходного кода, поэтому я принял решение написать один.
Выполнение
Суть эффективной реализации этого метода заключается в последовательном считывании счетчика отметок времени процессора до и после выполнения предварительной выборки. Даниэль Грусс любезно предоставил для этого высокоэффективный и открытый исходный код. Единственное изменение, которое я сделал (по предложению Янна Хорна), заключалось в том, чтобы переключиться на использование lfence вместо cpuid в качестве инструкции сериализации, поскольку cpuid эмулируется в средах виртуальных машин. На практике также стало очевидным, что нет необходимости выполнять какие-либо процедуры очистки кеша, чтобы наблюдать эффект побочного канала. Достаточно просто засечь время каждой попытки предварительной выборки.
Генерация времени предварительной выборки для всех 512 возможных слотов KASLR дает довольно много нечетких данных, нуждающихся в анализе. Чтобы свести к минимуму шум, берется несколько выборок каждого проверенного адреса, и минимальное значение из этого набора выборок используется в результатах в качестве репрезентативного значения для адреса. На ЦП Tiger Lake этот тест в основном выполнялся, поэтому для получения исключительно надежных результатов требовалось не более 16 образцов на слот. Идентификация минимального временного интервала предварительной выборки с низким разрешением сужает область поиска, избегая ложных срабатываний для кода обнаружения границ с более высоким разрешением, который находит точный адрес, по которому упреждающая выборка резко падает во время выполнения. Результатом этих усилий является PoC, который может правильно идентифицировать слайд KASLR на моем локальном компьютере с номером 99.
Этот код предварительной выборки действительно работает, чтобы найти расположение рандомизированных регионов CEA в предложенном патче Питера Зильстры. Тем не менее, путь к этому результату приводит к коду, который демонстрирует еще одну очень важную проблему — KASLR полностью скомпрометирован на x86 против локальных злоумышленников, и так было в течение последних нескольких лет и будет в неопределенном будущем. В настоящее время нет никаких планов по решению бесчисленных микроархитектурных проблем, которые приводят к таким сторонним каналам, как этот. В этой области необходима дальнейшая работа, чтобы сохранить целостность KASLR, или, в качестве альтернативы, возможно, пришло время признать, что KASLR больше не является эффективным средством защиты от локальных хакеров, и разработать защитный код и меры по защите последствий, которые принимают его ограничения.
Заключение
Этот эксплойт демонстрирует высоконадежный и независимый метод, который позволяет широкому спектру неконтролируемых произвольных примитивов записи выполнять код ядра на платформах x86. Несмотря на то, что эту технику эксплойта можно защитить из удаленного контекста, злоумышленник в локальном контексте может использовать известные побочные каналы микроархитектуры, чтобы обойти текущие средства защиты. Дополнительная работа в этой области может быть полезной для дальнейшего усложнения эксплуатации, например, выполнение рандомизации в стеке, чтобы смещение стека сохраненного состояния менялось при каждом принятом исключении IST. Однако на данный момент это остается жизнеспособной и мощной стратегией эксплойтов в x86 Linux.
Переведено специально для xss.pro
Автор перевода: yashechka
Источник: https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html
Настройка
Успешное срабатывание базовой уязвимости приводит к тому , что folio->mapping указывает на освобожденный объект anon_vma. Затем вызов madvise(..., MADV_PAGEOUT) можно использовать для повторного запуска доступа к освобожденному anon_vma в folio_lock_anon_vma_read() :
C:
struct anon_vma *folio_lock_anon_vma_read(struct folio *folio,
struct rmap_walk_control *rwc)
{
struct anon_vma *anon_vma = NULL;
struct anon_vma *root_anon_vma;
unsigned long anon_mapping;
rcu_read_lock();
anon_mapping = (unsigned long)READ_ONCE(folio->mapping);
if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON)
goto out;
if (!folio_mapped(folio))
goto out;
// anon_vma is dangling pointer
anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON);
// root_anon_vma is read from dangling pointer
root_anon_vma = READ_ONCE(anon_vma->root);
if (down_read_trylock(&root_anon_vma->rwsem)) {
[...]
if (!folio_mapped(folio)) { // false
[...]
}
goto out;
}
if (rwc && rwc->try_lock) { // true
anon_vma = NULL;
rwc->contended = true;
goto out;
}
[...]
out:
rcu_read_unlock();
return anon_vma; // return dangling pointer
}
Один из возможных приемов эксплойта — позволить функции вернуть указатель anon_vma и попытаться заставить последующие операции сделать что-то полезное. Вместо этого мы решили использовать вызов down_read_trylock() внутри функции для повреждения памяти по выбранному адресу, что мы можем сделать, если сможем управлять указателем root_anon_vma, который считывается из освобожденного anon_vma.
Управление указателем root_anon_vma означает восстановление освобожденного anon_vma с памятью, контролируемой злоумышленником. Структуры struct anon_vma выделяются из собственного кеша kmalloc, а это означает, что мы не можем просто освободить одну и восстановить ее с помощью другого объекта. Вместо этого мы возвращаем связанную slab-страницу anon_vma обратно в распределитель страниц ядра, следуя стратегии, очень похожей на описанную здесь - (https://googleprojectzero.blogspot.com/2021/10/how-simple-linux-kernel-memory.html). Освобождая все объекты anon_vma на slab-странице, а затем дампя частичный список свободных мест percpu slab-страницы, мы можем вызвать виртуальную память, ранее связанную с anon_vma для возврата обратно в распределитель страниц. Затем мы делаем распыление буферов канала, чтобы вернуть освобожденный anon_vma с памятью, контролируемой злоумышленником.
На данный момент мы обсудили, как превратить наше использование после освобождения в вызов down_read_trylock() для указателя, контролируемого злоумышленником. Реализация down_read_trylock() выглядит следующим образом:
C:
struct rw_semaphore {
atomic_long_t count;
atomic_long_t owner;
struct optimistic_spin_queue osq; /* spinner MCS lock */
raw_spinlock_t wait_lock;
struct list_head wait_list;
};
...
static inline int __down_read_trylock(struct rw_semaphore *sem)
{
long tmp;
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
tmp = atomic_long_read(&sem->count);
while (!(tmp & RWSEM_READ_FAILED_MASK)) {
if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
tmp + RWSEM_READER_BIAS)) {
rwsem_set_reader_owned(sem);
return 1;
}
}
return 0;
}
Было полезно эмулировать down_read_trylock() в unicorn, чтобы определить, как он ведет себя при разных значениях sem->count. Предполагая, что этот код работает с инертной и неизменной памятью, он будет увеличивать sem->count на 0x100, если 3 младших бита и самый старший бит не установлены. Это означает, что трудно изменить указатель ядра, и мы не можем изменить какие-либо значения, не выровненные по 8 байтам (поскольку они будут иметь установленный один или несколько из трех нижних битов). Кроме того, этот семафор позже разблокируется, в результате чего любая запись, которую мы выполняем, будет отменена в ближайшем будущем. Кроме того, на данный момент у нас нет установленной стратегии для определения слайда KASLR или определения адресов любых объектов, которые мы могли бы захотеть перезаписать нашим новообретенным примитивом. Оказывается, независимо от какой-либо рандомизации, имеющейся в настоящее время в ядре, существует прямая стратегия использования этой ошибки даже при такой ограниченной произвольной записи.
Повреждение стека…
В x86-64 Linux, когда ЦП выполняет определенные прерывания и исключения, он переключится на соответствующий стек, который сопоставлен со статическим и неслучайным виртуальным адресом, с другим стеком для разных типов исключений. Краткую документацию по этим стекам и их родительской структуре cpu_entry_area можно найти здесь - https://docs.kernel.org/x86/pti.html. Эти стеки чаще всего используются при входе в ядро из пользовательской среды, но они также используются для исключений, которые происходят в режиме ядра. Недавно мы видели записи KCTF, в которых злоумышленники используют нерандомизированную область cpu_entry_area для доступа к данным по известному виртуальному адресу в памяти, доступной ядру, даже при наличии SMAP и KASLR. Вы также можете использовать эти стеки для подделки контролируемых злоумышленником данных по известному виртуальному адресу ядра. Это работает, потому что содержимое регистра общего назначения задачи злоумышленника помещается непосредственно в этот стек, когда происходит переключение из режима пользователя в режим ядра из-за одного из этих исключений. Это также происходит, когда само ядро генерирует исключение таблицы стека прерываний и переключается на стек исключений, за исключением того, что в этом случае вместо этого помещаются GPR ядра. Эти проталкиваемые регистры позже используются для восстановления состояния ядра после обработки исключения. В случае исключения, вызванного пользовательской средой, содержимое регистра восстанавливается из стека задач.
Одним из примеров исключения IST является исключение DB, которое может быть запущено злоумышленником через аппаратную точку останова, связанные регистры которой описаны здесь - https://pdos.csail.mit.edu/6.828/2004/readings/i386/s12_02.htm . Аппаратные точки останова могут запускаться различными типами доступа к памяти, а именно чтением, записью и выборкой инструкций. Эти аппаратные точки останова могут быть установлены с помощью ptrace(2) и сохраняются во время выполнения режима ядра в контексте задачи, например, во время системного вызова. Это означает, что аппаратная точка останова, установленная злоумышленником, может быть активирована в режиме ядра, например, во время копирования_в/из_пользовательского вызова. Результирующее исключение будет сохранять и восстанавливать контекст ядра с помощью вышеупомянутого нерандомизированного стека исключений, и этот контекст ядра является исключительно хорошей целью для нашего произвольного примитива записи.
Любой из регистров, которые copy_to/from_user активно использует во время обработки аппаратной точки останова, можно повредить, используя наш примитив произвольной записи для перезаписи их сохраненных значений в стеке исключений. В этом случае размер вызова copy_user является интуитивно понятной целью. Значение размера постоянно сохраняется в регистре rcx, который будет сохраняться по одному и тому же виртуальному адресу каждый раз, когда срабатывает аппаратная точка останова. После повреждения этого сохраненного регистра нашим примитивом произвольной записи ядро восстановит rcx из стека исключений, как только он вернется обратно в copy_to/from_user. Поскольку rcx определяет количество байтов, которое должен скопировать copy_user, это повреждение приведет к тому, что ядро незаконно скопирует слишком много байтов между пользовательской средой и ядром.
… порождение повреждения стека
Стратегия атаки начинается следующим образом:
1. Форкается процесс Y из процесса X.
2. Процесс X отслеживает процесс Y, затем устанавливает аппаратную точку останова по известному виртуальному адресу [addr] в процессе Y.
3. Процесс Y делает большое количество вызовов uname(2), который вызывает copy_to_user из буфера стека ядра в [addr]. Это приводит к тому, что ядро постоянно запускает аппаратную точку наблюдения и входит в обработчик исключений БД, используя стек исключений БД для сохранения и восстановления состояния copy_to_user.
4. Одновременно выполняется много произвольных операций записи в известное расположение сохраненного значения rcx стека исключений БД, которое является сохраненной длиной copy_to_user процесса Y.
Стек исключений БД используется редко, поэтому маловероятно, что мы испортим какое-либо неожиданное состояние ядра с помощью ложного исключения БД, рассылая спам нашим произвольным примитивом записи. Техника также колоритна, но пропустить гонку просто означает испортить устаревшие данные стека. В этом случае мы просто пробуем снова. По моему опыту, для успешной победы в гонке редко требуется больше нескольких секунд.
При успешном искажении значения длины ядро скопирует большую часть стека текущей задачи обратно в пространство пользователя, включая файл cookie локального стека задачи и адреса возврата. Впоследствии мы можем инвертировать нашу технику и вместо этого атаковать вызовом copy_from_user. Вместо того, чтобы копировать слишком много байтов из стека задач ядра в область пользователя, мы вынуждаем ядро копировать слишком много байтов из области пользователя в стек задач ядра! Мы снова используем системный вызов prctl(2), который выполняет операцию copy_from_user.call буфера стека ядра. Теперь, искажая значение длины, мы создаем в этой функции условие переполнения буфера стека, которого раньше не было. Поскольку мы уже слили файл cookie стека и слайд KASLR, очень просто обойти обе защиты и перезаписать адрес возврата.
Завершение цепочки ROP для ядра оставляем читателю в качестве упражнения.
Получение слайда KASLR с предварительной выборкой
Сообщив об этой ошибке группе безопасности ядра Linux, мы предложили начать рандомизировать расположение percpu cpu_entry_area (CEA) и, следовательно, связанных стеков исключений и системных вызовов. Это эффективное средство защиты от злоумышленников, но его недостаточно, чтобы предотвратить использование преимущества локальным злоумышленником. 6 лет назад Дэниел Грусс и соавт. обнаружил новый, более надежный метод использования побочного канала синхронизации TLB в процессорах x86. Их результаты показали, что инструкции предварительной выборки, выполняемые в пользовательском режиме, удаляются со статистически значимыми различными задержками в зависимости от того, был ли запрошенный виртуальный адрес для предварительной выборки сопоставлен или не сопоставлен, даже если этот виртуальный адрес был сопоставлен только в режиме ядра. https://docs.kernel.org/x86/pti.html - был полезен для защиты этого побочного канала, однако большинство современных процессоров теперь имеют встроенную защиту от Meltdown, для решения которой был специально разработан kPTI, и, таким образом, kPTI (который имеет значительные последствия для производительности) отключен на современных микроархитектурах. Это решение означает, что снова можно воспользоваться преимуществом побочного канала предварительной выборки, чтобы обойти не только KASLR, но и защиту рандомизации области входа ЦП, сохраняя жизнеспособность метода эксплойта повреждения стека CEA против современных ЦП X86.
На удивление мало быстрых и надежных примеров этой техники обхода KASLR с предварительной выборкой, доступных в области открытого исходного кода, поэтому я принял решение написать один.
Выполнение
Суть эффективной реализации этого метода заключается в последовательном считывании счетчика отметок времени процессора до и после выполнения предварительной выборки. Даниэль Грусс любезно предоставил для этого высокоэффективный и открытый исходный код. Единственное изменение, которое я сделал (по предложению Янна Хорна), заключалось в том, чтобы переключиться на использование lfence вместо cpuid в качестве инструкции сериализации, поскольку cpuid эмулируется в средах виртуальных машин. На практике также стало очевидным, что нет необходимости выполнять какие-либо процедуры очистки кеша, чтобы наблюдать эффект побочного канала. Достаточно просто засечь время каждой попытки предварительной выборки.
Генерация времени предварительной выборки для всех 512 возможных слотов KASLR дает довольно много нечетких данных, нуждающихся в анализе. Чтобы свести к минимуму шум, берется несколько выборок каждого проверенного адреса, и минимальное значение из этого набора выборок используется в результатах в качестве репрезентативного значения для адреса. На ЦП Tiger Lake этот тест в основном выполнялся, поэтому для получения исключительно надежных результатов требовалось не более 16 образцов на слот. Идентификация минимального временного интервала предварительной выборки с низким разрешением сужает область поиска, избегая ложных срабатываний для кода обнаружения границ с более высоким разрешением, который находит точный адрес, по которому упреждающая выборка резко падает во время выполнения. Результатом этих усилий является PoC, который может правильно идентифицировать слайд KASLR на моем локальном компьютере с номером 99.
Этот код предварительной выборки действительно работает, чтобы найти расположение рандомизированных регионов CEA в предложенном патче Питера Зильстры. Тем не менее, путь к этому результату приводит к коду, который демонстрирует еще одну очень важную проблему — KASLR полностью скомпрометирован на x86 против локальных злоумышленников, и так было в течение последних нескольких лет и будет в неопределенном будущем. В настоящее время нет никаких планов по решению бесчисленных микроархитектурных проблем, которые приводят к таким сторонним каналам, как этот. В этой области необходима дальнейшая работа, чтобы сохранить целостность KASLR, или, в качестве альтернативы, возможно, пришло время признать, что KASLR больше не является эффективным средством защиты от локальных хакеров, и разработать защитный код и меры по защите последствий, которые принимают его ограничения.
Заключение
Этот эксплойт демонстрирует высоконадежный и независимый метод, который позволяет широкому спектру неконтролируемых произвольных примитивов записи выполнять код ядра на платформах x86. Несмотря на то, что эту технику эксплойта можно защитить из удаленного контекста, злоумышленник в локальном контексте может использовать известные побочные каналы микроархитектуры, чтобы обойти текущие средства защиты. Дополнительная работа в этой области может быть полезной для дальнейшего усложнения эксплуатации, например, выполнение рандомизации в стеке, чтобы смещение стека сохраненного состояния менялось при каждом принятом исключении IST. Однако на данный момент это остается жизнеспособной и мощной стратегией эксплойтов в x86 Linux.
Переведено специально для xss.pro
Автор перевода: yashechka
Источник: https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html