В данном исследовании проведем анализ уязвимости MS14-063, связанной с некорректной работой драйвера fastfat.sys и приводящей (по крайней мере, по словам Microsoft) к несанкционированному повышению привилегий. Данной уязвимости до недавнего времени были подвержены Win Server 2003/2008 и Win Vista (в Win7 данная дыра была исправлена давным давно, кстати говоря, но это уже совсем другая история — об этом подробнее рассказывается в статье на ресурсе xakep.ru). Тут же мы поговорим о том, какие возможности могла на самом деле предоставить данная уязвимость злоумышленнику, решившему реализовать атаку с помощью флешки с «битой» ФС FAT.
Итак, начнем с краткого описания устройства файловой системы FAT — работу с которой как раз реализует уязвимый драйвер fastfat.sys. Подробную документацию можно увидеть в спецификации (ссылка). Мы же остановимся только на интересующих нас положениях.
Иллюстрация построения файловой системы FAT, взятая с ресурса c-jump.com (ссылка)
В начале тома (по нулевому смещению) находится так называемый Boot Sector (на самом деле BIOS Parameter Block, или, сокращенно, BPB). Это структура, описывающая общее устройство тома и необходимая для того, чтобы том был правильно обработан драйвером. В этой структуре множество полезных полей, и к некоторым из них мы еще вернемся. Главное, что сейчас стоит отметить — это то, что мы говорим именно о файловой системе Fat32, т.к. в других реализациях FAT смещения в BPB будут другими, причем не только они.
Далее, за BPB, следует сама FAT — структура (FAT Data Structure), по сути своей, являющаяся односвязный списком кластеров файла (весь диск разбит на кластеры (причем в одном кластере единовременно могут находиться данные только одного файла), размер которых задается при форматировании диска — в свою очередь, кластеры разбиты на сектора)
Иллюстрация устройства области хранения информации (Data Area) на диске, взятая с ресурса technet.microsoft.com (ссылка)
На данном этапе необходимо вспомнить про BPB, а точнее обратить внимание на поле NumFats. Согласно спецификации, это поле задает количество копий FAT — структуры для каждого файла. В той же спецификации утверждается, что стандартное значение (которое, соответственно, гарантирует совместимость ФС с различными драйверами) для данного поля это 2, т.е. для каждого файла существует 2 копии FAT-структуры — это необходимо для предотвращения потери файла в случае появления bad sector’ов. Именно в обработке этого поля и кроется уязвимость. В случае, когда numfat > 2 происходит условный переход на участок кода, неверно выделяющий память в kernel pool (т.к. код исполняется внутри драйвера).
Иллюстрация участка уязвимого кода функции FatCommonWrite драйвера fastfat.sys, взятая с ресурса blog.beyondtrust.com — блога специалистов из компании BeyondTrust, также исследовавших данную уязвимость (ссылка)
Уязвимость находится в функции FatCommonWrite, а точнее в вызове ExAllocatePoolWithTag, где в качестве аргумента — необходимого места передается значение numfat — количество копий структур IoRuns, не умноженное на их размер. В коде, приведенном на иллюстрации, в регистре ecx находится указатель на структуру BPB, а по смещению 96h от него находится поле NumFats, которое, как видно из иллюстрации, попадает на стек в качестве аргумента NumberOfBytes у функции ExAllocatePoolWithTag (кстати говоря, в качестве патча, выпущенного от Microsoft, является как раз добавление недостающего умножения в блоке, приведенном на иллюстрации).
На данном этапе стоит подробнее поговорить об устройстве памяти драйверов (очень хороший разбор данного класса уязвимостей есть на хабре — ссылка). Резюмируя ее, имеем: память драйверов разбита на Pool’ы, каждый из которых является списком Chunk’ов, в которых, соответственно лежит необходимая драйверу информация.
Таким образом, здесь имеет место уязвимость типа kernel pool overflow в участке памяти, и, как следствие, существует возможность ее эксплуатации посредеством перебития chunk'а памяти другого драйвера, лежащего в памяти сразу за переполняемым chunk'ом.
Иллюстрация процесса переполнения chunk’а, взятая статьи на хабре о Kernel Pool Overflow (ссылка)
На данном этапе мы уже добились некоторого результата — мы корраптим память другого драйвера, что приводит к BSOD’у с ошибкой Bad Pool Header. Но этого мало, т.к. Memory Corruption это еще далеко не повышение привилегий.
Нашей целью является изменение header'а атакуемого (лежащего сразу за переполняемым) чанка на корректный, но не валидный, а произвольный (с целью захвата потока памяти драйвера). Это потенциально приведет к тому, что у атакующего будет возможность исполнить код от имени драйвера, т.е. с повышением привилегий. Однако, как показал анализ данных, которыми мы переполняем chunk, такой возможности в данной ситуации нет. Для обоснования этого рассмотрим структуру IoRuns, для которой как раз и выделяется память в уязвимой функции. Предположительно (опять же исходя из исходников Windows 2000), структура IoRuns служит инструментом, используемым при записи FAT-структур в файловую систему (т.к. функция FatCommonWrite вызывает как раз при записи новых файлов на диск, т.е. создание/изменение FAT-структур). Смысл в том, что все копии (их кол-во регламентируется полем NumFats в BPB) записываются асинхронно, для этого каждой копии ставится в соответствие свой экземпляр IoRuns, в котором указано, по какому оффсету писать(поля Vbo и Lbo) и сколько писать (ByteCount).
Иллюстрация цикла инициализации структур IoRuns (согласно иcходникам из WDK 8.0 Samples )
Таким образом, значения в полях структур IoRuns не так просто подменить, причем все остальные структуры будут зависеть, а вернее, однозначно вычисляться, из значений полей первой структуры. Что мы имеем: первое поле — смещение, по которому начинается запись копий FAT-структур, второе поле — смещение, по которому записывается данная копия (согласно значению литератора), третье поле — вероятно, нужно для обработки дополнительного пролога к FAT-структуре. И последнее поле задается напрямую из BPB — оно равно полю BPB_BytsPerSec, т.к. каждая копия FAT-структура занимает один сектор.
Отсюда следует, что задать такие данные, которые перепишут следующий chunk и оставят его корректным, попросту невозможно, т.к. мы контролируем всего одно ULONG поле, что приводит к выводу о невозможности реализации повышения привилегий.
Подведем итог: данное исследование приводит к вопросу, правы ли специалисты из Microsoft, говоря о том, что MS14-063 действительно существенна и может привести к эскалации привилегий? Согласно проведенному анализу, возможность “навредить” у данной уязвимости имеется только в вызове BSOD, но о каких либо эскалациях говорить не приходится.
Итак, начнем с краткого описания устройства файловой системы FAT — работу с которой как раз реализует уязвимый драйвер fastfat.sys. Подробную документацию можно увидеть в спецификации (ссылка). Мы же остановимся только на интересующих нас положениях.
Иллюстрация построения файловой системы FAT, взятая с ресурса c-jump.com (ссылка)
В начале тома (по нулевому смещению) находится так называемый Boot Sector (на самом деле BIOS Parameter Block, или, сокращенно, BPB). Это структура, описывающая общее устройство тома и необходимая для того, чтобы том был правильно обработан драйвером. В этой структуре множество полезных полей, и к некоторым из них мы еще вернемся. Главное, что сейчас стоит отметить — это то, что мы говорим именно о файловой системе Fat32, т.к. в других реализациях FAT смещения в BPB будут другими, причем не только они.
Далее, за BPB, следует сама FAT — структура (FAT Data Structure), по сути своей, являющаяся односвязный списком кластеров файла (весь диск разбит на кластеры (причем в одном кластере единовременно могут находиться данные только одного файла), размер которых задается при форматировании диска — в свою очередь, кластеры разбиты на сектора)
Иллюстрация устройства области хранения информации (Data Area) на диске, взятая с ресурса technet.microsoft.com (ссылка)
На данном этапе необходимо вспомнить про BPB, а точнее обратить внимание на поле NumFats. Согласно спецификации, это поле задает количество копий FAT — структуры для каждого файла. В той же спецификации утверждается, что стандартное значение (которое, соответственно, гарантирует совместимость ФС с различными драйверами) для данного поля это 2, т.е. для каждого файла существует 2 копии FAT-структуры — это необходимо для предотвращения потери файла в случае появления bad sector’ов. Именно в обработке этого поля и кроется уязвимость. В случае, когда numfat > 2 происходит условный переход на участок кода, неверно выделяющий память в kernel pool (т.к. код исполняется внутри драйвера).
Иллюстрация участка уязвимого кода функции FatCommonWrite драйвера fastfat.sys, взятая с ресурса blog.beyondtrust.com — блога специалистов из компании BeyondTrust, также исследовавших данную уязвимость (ссылка)
Уязвимость находится в функции FatCommonWrite, а точнее в вызове ExAllocatePoolWithTag, где в качестве аргумента — необходимого места передается значение numfat — количество копий структур IoRuns, не умноженное на их размер. В коде, приведенном на иллюстрации, в регистре ecx находится указатель на структуру BPB, а по смещению 96h от него находится поле NumFats, которое, как видно из иллюстрации, попадает на стек в качестве аргумента NumberOfBytes у функции ExAllocatePoolWithTag (кстати говоря, в качестве патча, выпущенного от Microsoft, является как раз добавление недостающего умножения в блоке, приведенном на иллюстрации).
На данном этапе стоит подробнее поговорить об устройстве памяти драйверов (очень хороший разбор данного класса уязвимостей есть на хабре — ссылка). Резюмируя ее, имеем: память драйверов разбита на Pool’ы, каждый из которых является списком Chunk’ов, в которых, соответственно лежит необходимая драйверу информация.
Таким образом, здесь имеет место уязвимость типа kernel pool overflow в участке памяти, и, как следствие, существует возможность ее эксплуатации посредеством перебития chunk'а памяти другого драйвера, лежащего в памяти сразу за переполняемым chunk'ом.
Иллюстрация процесса переполнения chunk’а, взятая статьи на хабре о Kernel Pool Overflow (ссылка)
На данном этапе мы уже добились некоторого результата — мы корраптим память другого драйвера, что приводит к BSOD’у с ошибкой Bad Pool Header. Но этого мало, т.к. Memory Corruption это еще далеко не повышение привилегий.
Нашей целью является изменение header'а атакуемого (лежащего сразу за переполняемым) чанка на корректный, но не валидный, а произвольный (с целью захвата потока памяти драйвера). Это потенциально приведет к тому, что у атакующего будет возможность исполнить код от имени драйвера, т.е. с повышением привилегий. Однако, как показал анализ данных, которыми мы переполняем chunk, такой возможности в данной ситуации нет. Для обоснования этого рассмотрим структуру IoRuns, для которой как раз и выделяется память в уязвимой функции. Предположительно (опять же исходя из исходников Windows 2000), структура IoRuns служит инструментом, используемым при записи FAT-структур в файловую систему (т.к. функция FatCommonWrite вызывает как раз при записи новых файлов на диск, т.е. создание/изменение FAT-структур). Смысл в том, что все копии (их кол-во регламентируется полем NumFats в BPB) записываются асинхронно, для этого каждой копии ставится в соответствие свой экземпляр IoRuns, в котором указано, по какому оффсету писать(поля Vbo и Lbo) и сколько писать (ByteCount).
Иллюстрация цикла инициализации структур IoRuns (согласно иcходникам из WDK 8.0 Samples )
Таким образом, значения в полях структур IoRuns не так просто подменить, причем все остальные структуры будут зависеть, а вернее, однозначно вычисляться, из значений полей первой структуры. Что мы имеем: первое поле — смещение, по которому начинается запись копий FAT-структур, второе поле — смещение, по которому записывается данная копия (согласно значению литератора), третье поле — вероятно, нужно для обработки дополнительного пролога к FAT-структуре. И последнее поле задается напрямую из BPB — оно равно полю BPB_BytsPerSec, т.к. каждая копия FAT-структура занимает один сектор.
Отсюда следует, что задать такие данные, которые перепишут следующий chunk и оставят его корректным, попросту невозможно, т.к. мы контролируем всего одно ULONG поле, что приводит к выводу о невозможности реализации повышения привилегий.
Подведем итог: данное исследование приводит к вопросу, правы ли специалисты из Microsoft, говоря о том, что MS14-063 действительно существенна и может привести к эскалации привилегий? Согласно проведенному анализу, возможность “навредить” у данной уязвимости имеется только в вызове BSOD, но о каких либо эскалациях говорить не приходится.