Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #79, ИЮЛЬ 2005 г.

Захват нулевого кольца

Крис Касперски ака мыщъх

Хакер, номер #079, стр. 079-104-4


К сожалению, даже простейший эксплоит занимает слишком много места, и поэтому не может быть здесь приведен, однако, его исходный код легко найти в Интернете. Оригинальная версия с подробным описанием техники взлома лежит на: www.isec.pl/vulnerabilities/isec-0021-uselib.txt.

[Получаем root'а на многопроцессорных машинах]

А вот другая интересная уязвимость, затрагивающая большое количество ядер с версиями 2.4/2.6 и поражающая многопроцессорные машины. Обнаруженная в самом начале 2005 года, она все еще остается актуальной, поскольку далеко не все администраторы установили соответствующие заплатки, а многопроцессорные машины (включая микропроцессоры с поддержкой Hyper-Threading) в наши дни скорее правило, чем редкость.

Во всем виноват обработчик ошибок доступа к страницам (page fault handler), который вызывается всякий раз, когда приложение обращается к невыделенной или защищенной странице памяти. Не все ошибки одинаково фатальны. В частности, Linux (как и большинство других систем) выделяет стековую память не сразу, а по частям. На вершине выделенной памяти находится станица, доступ к которой умышленно запрещен. Она называется "сторожевой" (GUARD_PAGE). Стек постепенно растет и в какой-то момент "врезается" в сторожевую страницу, возбуждая исключение. Его перехватывает page fault handler, и операционная система выделяет стеку некоторое количество памяти, перемещая сторожевую страницу наверх. На однопроцессорных машинах эта схема работает как часы, а вот на многопроцессорных...

Ключевой фрагмент файла /mm/fault.c, содержащий ошибку синхронизации

down_read(&mm->mmap_sem); /* warn */

vma = find_vma(mm, address);

if (!vma)

goto bad_area;

if (vma->vm_start <= address)

goto good_area;

if (!(vma->vm_flags & VM_GROWSDOWN))

goto bad_area;

if (error_code & 4) {

if (address + 32 < regs->esp) /* warn */

goto bad_area;

}

if (expand_stack(vma, address))

goto bad_area;

Поскольку page fault handler выполняется с семафором, доступным только на чтение, несколько конкурирующих потоков могут одновременно войти в обработчик за строкой /* warn */. Рассмотрим, что произойдет, если два потока, разделяющих одну и туже виртуальную память, одновременно вызовут page fault handler. Приблизительный сценарий атаки выглядит так: поток 1 обращается к сторожевой странице и вызывает исключение fault_1. Поток 2 обращается к странице GUARD_PAGE + PAGE_SIZE и вызывает исключение fault_2. Состояние виртуальной памяти на момент вызова page fault handler'а двумя потоками:

[ NOPAGE ] [fault_1 ] [ VMA ] ---> higher addresses

[fault_2 ] [ NOPAGE ] [ VMA ]

Если поток 2 опередит поток 1 и первым выделит свою страницу PAGE1, поток 1 вызовет серьезное нарушение в работе менеджера виртуальной памяти, поскольку нижняя граница стека теперь находится выше fault_2, и потому страница PAGE2 реально не выделяется, но становится доступной на чтение/запись обоим потокам, причем после завершения процесса она не будет удалена! Состояние виртуальной памяти на момент выхода из page fault handler'а:

Назад на стр. 079-104-3  Содержание  Вперед на стр. 079-104-5