Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Летом я выступил с докладом по этой теме на конференции ZeroNights 2021.
Слайды доклада в PDF
В предыдущей статье я описал прототип эксплоита для локального повышения привилегий на Fedora
для платформы x86_64
. Я рассказал, как состояние гонки в реализации виртуальных сокетов ядра Linux может привести к повреждению четырех байтов ядерной памяти. Я показал, как атакующий может шаг за шагом превратить эту ошибку в произвольное чтение‑запись памяти ядра и повысить свои привилегии в системе. Но некоторые ограничения этого способа повысить привилегии мешали мне экспериментировать в системе под защитой LKRG. Я решил продолжить исследование и выяснить, можно ли их устранить.
Мой прототип эксплоита выполнял произвольную запись с помощью перехвата потока управления при вызове деструктора destructor_arg
в атакованном ядерном объекте sk_buff
.
Этот деструктор имеет следующий прототип:
void (*callback)(struct ubuf_info *, bool zerocopy_success);
Когда ядро вызывает его в функции skb_zcopy_clear(), регистр RDI
содержит первый аргумент функции. Это адрес самой структуры ubuf_info
. А регистр RSI
хранит единицу в качестве второго аргумента функции.
Содержимое этой структуры ubuf_info
контролируется эксплоитом. Однако первые восемь байтов в ней должны быть заняты адресом функции‑деструктора, как видно на схеме. В этом и есть основное ограничение. Из‑за него ROP-гаджет для переключения ядерного стека на контролируемую область памяти (stack pivoting) должен выглядеть примерно так:
mov rsp, qword ptr [rdi + 8] ; ret
К сожалению, ничего похожего в ядре Fedora vmlinuz-5.
обнаружить не удалось. Но зато с помощью ROPgadget я нашел такой гаджет, который удовлетворяет этим ограничениям и выполняет запись ядерной памяти вообще без переключения ядерного стека:
mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rdx + rcx*8], rsi ; ret
Как сказано выше, RDI
— это адрес ядерной памяти, содержимое которой контролирует атакующий. В регистре RSI
содержится единица, а в RCX
— ноль. То есть этот гаджет записывает семь нулевых байтов и один байт с единицей по адресу, который задает атакующий. Как выполнить повышение привилегий процесса с помощью этого ROP-гаджета? Мой прототип эксплоита записывает ноль в поля uid
, gid
, effective
и effective
структуры cred
.
Мне удалось придумать хоть и странный, но вполне рабочий эксплоит‑примитив. При этом я не был полностью удовлетворен этим решением, потому что оно не давало возможности полноценного ROP. Кроме того, приходилось выполнять перехват потока управления дважды, чтобы перезаписать все необходимые поля в struct
. Это делало прототип эксплоита менее надежным. Поэтому я решил немного отдохнуть и продолжить исследование.
Первым делом я решил еще раз посмотреть на состояние регистров процессора в момент перехвата потока управления. Я поставил точку останова в функции skb_zcopy_clear(), которая вызывает обработчик callback
из destructor_arg
:
$ gdb vmlinux
gdb-peda$ target remote :1234
gdb-peda$ break ./include/linux/skbuff.h:1481
Вот что отладчик показывает прямо перед перехватом потока управления.
Какие ядерные адреса хранятся в регистрах процессора? RDI
и R8
содержат адрес ubuf_info
. Разыменование этого указателя дает указатель на функцию callback
, который загружен в регистр RAX
. В регистре R9
содержится некоторый указатель на память в ядерном стеке (его значение близко к значению RSP
). В регистрах R12
и R14
находятся какие‑то адреса памяти в ядерной куче, и мне не удалось выяснить, на какие объекты они ссылаются.
А вот регистр RBP
, как оказалось, содержит адрес skb_shared_info
. Это адрес моего объекта sk_buff
плюс отступ SKB_SHINFO_OFFSET
, который равен 3776
или 0xec0
(больше деталей в предыдущей статье). Этот адрес дал мне надежду на успех, потому что он указывает на память, содержимое которой находится под контролем эксплоита. Я начал искать ROP/JOP-гаджеты, использующие RBP
.
Я стал просматривать все доступные гаджеты с участием RBP
и нашел множество JOP-гаджетов, похожих на этот:
0xffffffff81711d33 : xchg eax, esp ; jmp qword ptr [rbp + 0x48]
Адрес RBP
также указывает на ядерную память под контролем атакующего. Я понял, что могу выполнить stack pivoting с помощью цепочки таких JOP-гаджетов, после чего выполнить полноценную ROP-цепочку. Отлично!
Для быстрого эксперимента я взял этот гаджет:
xchg eax, esp ; jmp qword ptr [rbp + 0x48]
Он переключает ядерный стек на память в пользовательском пространстве. Сначала я удостоверился, что гаджет действительно находится в коде ядра:
$ gdb vmlinux
gdb-peda$ disassemble 0xffffffff81711d33
Dump of assembler code for function acpi_idle_lpi_enter:
0xffffffff81711d30 <+0>: call
0xffffffff810611c0 <fentry
0xffffffff81711d35 <+5>: mov
rcx,QWORD PTR gs:[rip+0x7e915f4b]
0xffffffff81711d3d <+13>:
test
rcx,rcx
0xffffffff81711d40 <+16>:
je
0xffffffff81711d5e Андрея Коновалова, известного исследователя безопасности Linux, не сталкивался ли он с таким эффектом. Андрей обратил внимание, что байты кода, которые распечатало ядро, отличались от вывода утилиты objdump для исполняемого файла ядра.
Это был первый случай в моей практике с ядром Linux, когда дамп кода в ядерном журнале оказался полезен. Я подключился отладчиком к работающему ядру и обнаружил, что код функции acpi_idle_lpi_enter(
действительно изменился:
$ gdb vmlinux
gdb-peda$ target remote :1234
gdb-peda$ disassemble 0xffffffff81711d33
Dump of assembler code for function acpi_idle_lpi_enter:
0xffffffff81711d30 <+0>: nop
DWORD PTR [rax+rax*1+0x0]
0xffffffff81711d35 <+5>: mov
rcx,QWORD PTR gs:[rip+0x7e915f4b]
0xffffffff81711d3d <+13>:
test
rcx,rcx
0xffffffff81711d40 <+16>:
je
0xffffffff81711d5e CONFIG_DYNAMIC_FTRACE. Он также испортил множество других JOP-гаджетов, на которые я рассчитывал! Чтобы не столкнуться с этим снова, я решил попробовать искать нужные ROP/JOP-гаджеты в памяти ядра живой виртуальной машины.
Сначала я опробовал команду ropsearch
из инструмента gdb-peda, но у нее оказалась слишком ограниченная функциональность. Тогда я зашел с другой стороны и сделал снимок всей области памяти с ядерным кодом с помощью команды gdb-peda
. В первую очередь нужно было определить расположение ядерного кода в памяти:
[
ffffffff81000000 T _text[
ffffffff81e026d7 T _etext
Затем я сделал снимок памяти между адресами _text
и _etext
:
gdb-peda$ dumpmem kerndump 0xffffffff81000000 0xffffffff81e03000
Dumped 14692352 bytes to 'kerndump'
После этого я применил к полученному файлу утилиту ROPgadget. Она может искать ROP/JOP-гаджеты в сыром снимке памяти, если задать дополнительные опции (спасибо за подсказку моему другу Максиму Горячему, известному исследователю безопасности железа):
# ./
Теперь я был готов составить JOP/ROP-цепочку.
|
|