Прим. Wunder Fund: наш СТО Эмиль по совместительству является известным white-hat хакером и специалистом по информационной безопасности, и эту статью он предложил как хорошее знакомство с фаззером afl и вообще с фаззингом как таковым. В первой части этой серии статей я рассказал о том, как организовать фаззинг Apache HTTP Server с привлечением кастомных мутаторов. Во втором материале я раскрыл вопрос создания перехватчиков ASAN, которые позволяют выявлять ошибки при использовании собственных реализаций пулов памяти. Эта статья, третья и последняя, посвящена результатам моих исследований. Я расскажу тут об обнаруженных мной уязвимостях Apache. Эту ошибку можно вызвать, поместив в Cookie пару ключ/значение, оба элемента которой равны NULL. В этом примере можно заметить, что первую позицию в Cookie занимает ключ session и значение choko. Во второй позиции ключом является admin-user, а значением — число 2. А вот третья позиция представлена пустыми ключом и значением. Что здесь за проблема? Если посмотреть на следующий фрагмент кода, там можно заметить два вызова apr_strtok, направленных на извлечение первой и второй строки (ключа и значения): А вот что происходит в функции apr_strtok в том случае, если первым её аргументом является NULL: Тут можно видеть, что в цикле while делается попытка разыменовать первый аргумент функции (указатель str). Если этот аргумент представлен значением NULL — это приведёт к ошибке разыменования NULL. Кроме того, именно это происходит в инструкции char *val = apr_strtok(NULL, psep, &plast);, когда предыдущий ключ тоже представлен NULL. Воспользоваться этой ошибкой можно при включённом модуле mod_session. Эта уязвимость может привести к отказу в обслуживании на уровне дочернего потока процесса, повлияв на другие его потоки. Для того чтобы воспользоваться этой ошибкой — нужно, чтобы был включён модуль mod_auth_digest, и чтобы приложение использовало бы метод аутентификации DIGEST. Для вызова ошибки нужно назначить полю nonce специфический набор значений: Как видите, поле nonce содержит значение в кодировке BASE64. Для декодирования этого значения функция check_nonce выполняет следующий вызов: Здесь nonce_time.arr — это локальный массив размером 8 байтов. Посмотрим на код функции apr_base64_decode_binary: В обычных обстоятельствах в переменной nprbytes окажется значение 11, а цикл while будет выполнен два раза, записав всего 8 байтов в массив bufplain (6 + 2). Но если дата имеет неправильный формат, при вычислении значения переменной nprbytes может получиться число 12. В результате в этих случаях цикл while будет выполнен три раза, в массив bufplain будет записано 9 байтов. Вследствие этого программа запишет 1 байт за пределами локального массива nonce_time.arr, перезаписав 1 байт стека программы (так и происходит ошибка неучтённой единицы). Тут у нас имеется ошибка, связанная с использованием памяти после её освобождения (Use After Free, UAF) в функции cleanup_tables. Посмотрим на код этой функции: Она вызывает функцию apr_rmm_destroy для освобождения блока памяти client_rmm. Но тут есть одна проблема: в определённых обстоятельствах этот блок памяти уже может быть освобождён функцией apr_allocator_destroy (её код тут не показан). В результате программа пытается обратиться к недействительному адресу памяти. Это приводит к появлению уязвимости UAF. Тут важно заметить, что эта уязвимость может быть активирована лишь в режиме ONE_PROCESS. В данном случае перед нами ошибка, связанная с записью данных за пределами допустимого диапазона в куче, воздействующая на функцию ap_escape_quotes. Эта функция экранирует кавычки в предоставленной ей строке. Источник данной ошибки — несовпадение длины входной строки и буфера outstring, память под который «выделена» с помощью malloc. В следующем фрагменте кода показано вычисление длины входной строки: А вот — вычисление размера outstring: Как видите, при вычислении размеров этих сущностей используется различная логика. В результате, если функции ap_escape_quotes предоставить особые входные данные, возникает возможность записи данных за пределами массива outchr. Об этой ошибке, за несколько дней до того, как я её обнаружил, сообщили исследователи из проекта Google OSS-Fuzz. Теперь хочу рассказать кое о чём совершенно отличном от того, о чём уже рассказывал. В данном случае ошибка представлена состоянием гонок, которое ведёт к UAF и воздействует на Apache Core. В ходе моих фаззинг-исследований я столкнулся с множеством невоспроизводимых UAF-падений программы. Глубже проанализировав ситуацию, я обнаружил нечто вроде состояния гонок между apr_allocator_destroy и allocator_alloc. Всё указывало на то, что эти функции в конкурентных сценариях могут не отличаться потокобезопасностью. Это может привести к повреждениям в некоторых узлах памяти и, иногда, к тому, что программа пытается освободить память, которая уже находится в пуле free. Этот баг чем-то cхож с багом ProFTPd, о котором я сообщал год назад (CVE-2020-9273). Ниже представлен пример соответствующего стек-трейса ASAN: Эта проблема не нова. О похожих ошибках сообщал в 2018 году Ханно Бок (hanno). Тут можно найти его отчёты. Я, занимаясь фаззингом, обнаружил ещё кое-какие небольшие баги. Об одном из них я сейчас расскажу. Это — переполнение целочисленной переменной в функции Session_Identity_Decode. Этот баг не относится к разряду опасных, но я полагаю, что интересно будет показать пример того, как легко его вызвать. Мы отправляем WebDav-запрос LOCK, нацеленный на MOD_DAV, передавая очень большое значение Timeout (Second-41000000004100000000): В следующем фрагменте кода можно видеть такую конструкцию: Здесь выполняется сложение двух 32-битных целочисленных значений, результат операции оказывается в переменной того же типа. Если складываемые значения достаточно велики — при возврате результата операции произойдёт переполнение. Так как этот баг вызывается при выполнении запроса LOCK — для того, чтобы он проявился, должен быть включён модуль MOD_DAV. Хотя безопасность Apache HTTP Server уже очень хорошо изучена, учитывая недавно обнаруженные уязвимости, связанные с обходом путей и раскрытием файлов (CVE-2021-41773 и CVE-2021-42013), ясно, что в этой программе ещё можно обнаружить новые критические уязвимости. Проводя это исследование, я хотел сделать собственный вклад в улучшение безопасности Apache HTTP Server, и показать, что фаззинг можно применять для поиска уязвимостей в одном из самых популярных опенсорсных проектов современности. Я, в то же время, надеюсь, что у меня получилось поделиться знаниями, приобретёнными в ходе этой работы, с моими читателями. Эта статья закрывает цикл «Фаззинг сокетов». В следующем материале я собираюсь рассказать о фаззинге JavaScript-движков. До новых встреч! Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки. Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь. Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров. Присоединяйтесь к нашей команде.