Изображение: Unsplash
Как было описано в предыдущей статье про CVE-2019-0726, иногда поиск деталей об уже известной уязвимости приводит к обнаружению новой уязвимости. А в некоторых случаях таких новых уязвимостей оказывается больше одной.
В статье рассматривались две функции библиотеки dhcpcore.dll: вскользь упомянутая UpdateDomainSearchOption и более подробно разобранная вызываемая ею DecodeDomainSearchListData. Как всегда происходит при поиске уязвимостей, даже если важные выводы в конце сводятся к одной-двум функциям, в процессе разбора приходится просмотреть гораздо большее количество кода. И порой глаз цепляется за мелочи, которые не имеют отношения к текущей задаче, но могут иметь самостоятельное значение или пригодиться позже. Пусть в данный момент нет времени обращать на них внимание, но такие мелочи откладываются на подкорке и, если по истечении некоторого срока появляется возможность вернуться к ним и проверить свои догадки, вновь всплывают в сознании.
Так и произошло на этот раз. При изучении функции DhcpExtractFullOptions, отвечающей за обработку всех опций, заданных в DHCP-ответе от сервера, в частности вызывающей UpdateDomainSearchOption, внимание сразу привлекают два массива на стеке по 256 элементов каждый:
При этом не заметно присутствия каких-либо проверок, ограничивающих значения итераторов данных массивов. Так как в тот момент мы разбирали другую уязвимость, эта информация к делу не относилась. Поэтому оставалось лишь запомнить это место в коде, чтобы возвратиться к нему позже.
Анализ
Проходит пара недель, и мы вновь вспоминаем про обратившую на себя внимание ранее функцию DhcpExtractFullOptions. Обращаемся к ней в дизассемблере, причесываем ранее не до конца разобранные куски кода и пытаемся понять, для чего используются два заинтриговавших нас статических массива.
В самом начале исполнения функции массивы и их итераторы обнуляются:
Функция занимается разбором всех опций в полученном от DHCP-сервера пакете, сбором информации из них и ее последующей обработкой. Помимо этого, по результатам разбора она также записывает соответствующее событие в сервис ETW (Event Tracing for Windows). Именно в логировании события и принимают участие интересующие нас буферы. Вместе с большим количеством других данных они передаются в процедуру EtwEventWriteTransfer. Работа по подготовке всех данных для логирования довольно объемна и не имеет большого значения для рассматриваемой нами уязвимости, поэтому обойдемся без иллюстраций.
Важнее определить, как эти буферы заполняются. Происходит заполнение в цикле разбора опций. Сначала для текущей поступившей на обработку опции вызывается функция с говорящим названием ParseDhcpv4Option, которая либо заполняет поля объекта dhcp_pointers на основании поступивших данных, либо делает пометку о незнакомой опции, когда встречает идентификатор опции со значением, для которого отсутствует обработчик.
По возвращении из ParseDhcpv4Option значение идентификатора текущей опции option_tag записывается в следующий элемент массива all_tags, первого из интересующих нас массивов. Если функция встретила незнакомую опцию и, соответственно, не выставила флаг is_known_option, то значение идентификатора записывается также и в следующий элемент второго массива — unknown_tags. Естественно, осмысленные названия переменные в этой статье получили уже по результатам разбора кода.
Таким образом, массив all_tags хранит теги всех опций из поступившего сообщения, а массив unknown_tags — теги только для незнакомых парсеру опций. При этом проверка значений индексов этих массивов отсутствует вовсе. Следовательно, значения таких индексов могут превышать 256 и приводить к записи за пределы отведенной на стеке под массивы памяти. Для переполнения первого массива достаточно отправить от DHCP-сервера пакет с количеством опций, превышающим 256. То же самое справедливо и для второго массива с той лишь разницей, что отправлять следует опции, которые клиент не умеет обрабатывать.
Эксплуатация
Попробуем теперь на практическом примере убедиться в том, что сделанные нами выводы верны. Для начала обратим внимание на то, что теги опций занимают один байт, в то время как элементы массивов имеют тип int, то есть являются четырехбайтовыми. Таким образом, мы имеем переполнение, в котором мы контролируем каждый четвертый байт, а остальные при перезаписи обнуляются.
Для проверки нашего предположения проще всего будет перезаписать хранящуюся на стеке security cookie рассматриваемой функции, что вызовет исключение, связанное с проверкой безопасности. Смоделируем ситуацию, в которой DHCP-сервер отправляет достаточное для перезаписи количество опций. Пускай это будут 0x1a0 (416) опций с идентификатором 0xaa и нулевым размером. Таким образом, каждая опция занимает два байта, и полный размер пакета со всеми заголовками составит 1100—1200 байт. Это значение находится в пределах MTU для Ethernet, следовательно, имеются основания полагать, что сообщение не будет фрагментировано, что позволит нам избежать возможных неблагоприятных эффектов.
Отправляем сформированный описанным способом пакет в ответ на запрос от DHCP-клиента и перехватываем на клиентской машине исключение в соответствующем процессе svchost.exe:
Как видно из трассировки стека, идентификаторами опций из нашего пакета были переписаны и stack cookie, и адрес возврата из функции.
Конечно, создание рабочего эксплойта под данную ошибку потребует от злоумышленника достаточно больших усилий. На современных системах переполнение буфера на стеке является довольно сложной и трудоемкой в эксплуатации уязвимостью благодаря всем существующим нынче защитным механизмам. С другой стороны, не следует забывать, что все эти механизмы либо защищают от перезаписи адреса возврата и обработчиков исключений, либо запрещают исполнение кода в непредназначенных для этого регионах памяти, либо мешают предсказывать адреса. Они, например, никак не могут помочь против перезаписи хранящихся на стеке между переполняемым буфером и адресом возврата локальных переменных. И рассматриваемая функция DhcpExtractFullOptions содержит в этом интервале несколько потенциально опасных переменных.
Вновь пишем в Microsoft об обнаруженной ошибке. После непродолжительной переписки и занявшего примерно неделю анализа заявки получаем ответ, что для описанной уязвимости готовится CVE-идентификатор, исправление планируется к выпуску в марте, а информация об уязвимости уже имеется в Microsoft, была сообщена кем-то ранее. Факт, вообще говоря, неудивительный, ведь ошибка лежит буквально на поверхности, а буферы, не содержащие граничных проверок индексов, всегда обращают на себя внимание первыми и часто могут быть обнаружены автоматическими средствами анализа.
В марте, как и было заявлено, выходит обновление, исправляющее описанную ошибку, получившую идентификатор CVE-2019-0697. Исследователем, сообщившим информацию ранее, оказался Mitch Adair, тот самый сотрудник Microsoft, что обнаружил и исправленную в январе DHCP-уязвимость CVE-2019-0547.
Автор: Михаил Цветков, специалист отдела анализа приложений Positive Technologies.
Автор: ptsecurity