В статье показана недостаточность
защиты сетевых соединений между рабочими
станциями и серверами Windows NT, организованных
посредством интерфейса NPFS. Этот интерфейс имеет
ряд слабостей, позволяющих осуществлять
различные удаленные атаки. Предложены две атаки,
одна из которых относится к классу "отказ в
обслуживании", а другая позволяет получать
обычному пользователю права администратора
путем перехвата административных сетевых
соединений. Рассматриваются возможности
усиления защиты NPFS-соединений в Windows NT.
Что
такое NPFS.
Аббревиатура NPFS расшифровывается как Named Pipe File
System. Несмотря на то, что в этом названии
присутствуют слова "file system", назвать NPFS
файловой системой можно только с очень большой
натяжкой. Драйвер npfs.sys, хотя и работает в
соответствии со спецификациями драйвера
файловой системы, не управляет никакими файлами.
Вместо этого npfs.sys управляет так называемыми каналами
(named pipes). Вместе с файлами, дисковыми
директориями, устройствами и почтовыми ящиками (mailslots)
каналы относятся к классу файловых объектов.
Большинство функций, предназначенных для работы
с файлами (в том числе CreateFile, ReadFile и WriteFile),
работают и с каналами.
Канал можно представить себе как некую
виртуальную трубу, по которой перетекает
информация от одного процесса к другому.
Информация может передаваться как в одну сторону
(однонаправленный канал), так и в обе стороны (двунаправленный
или дуплексный канал). Хотя каналы Windows NT
очень похожи на аналогичные объекты UNIX,
техническая реализация каналов в Windows NT и UNIX
существенно различается.
Создание канала происходит следующим образом.
- Процесс-сервер создает канал с помощью функции
программного интерфейса Win32 CreateNamedPipe. Канал может
быть создан только на локальном компьютере.
- Процесс-сервер активизирует канал с помощью
функции ConnectNamedPipe. Теперь к каналу могут
подключаться клиенты.
- Процесс-клиент подключается к каналу
посредством вызова функции CreateFile. Параметры
функции отличаются от обычных только именем
открываемого объекта, которое имеет вид \computer_namepipepipe_name.
Вместо имени компьютера может использоваться
символ ‘.’ (точка), в этом случае речь идет о
локальном компьютере.
После выполнения перечисленных действий
процесс-клиент и процесс-сервер могут
обмениваться информацией посредством канала с
помощью функций ReadFile и WriteFile. Все, что один
участник информационного обмена записывает в
канал функцией WriteFile, может быть прочтено другим
участником посредством вызова функции ReadFile.
Клиент может отключиться от канала в любой
момент с помощью функции CloseHandle. Сервер может
отключить клиента в любой момент с помощью
функции DisconnectNamedPipe. После прекращения связи с
клиентом сервер может повторно использовать
канал с помощью повторного вызова функции
ConnectNamedPipe.
С помощью одного и того же канала сервер может
одновременно обслуживать нескольких клиентов.
Для этого сервер должен создать несколько экземпляров
канала, вызвав функцию CreateNamedPipe нужное
количество раз, при этом в каждом вызове CreateNamedPipe
должно быть указано одно и то же имя канала. Если
канал имеет несколько экземпляров, клиент может
быть подключен к любому свободному (не занятому
другим клиентом) экземпляру этого канала. Как
показывают эксперименты, клиент всегда
подключается к тому экземпляру канала, который
был создан или освобожден раньше других. Другими
словами, свободные экземпляры канала
подключаются к клиентам в порядке "живой"
очереди.
Интерфейс NPFS имеет много других возможностей,
поддерживаются асинхронная передача информации,
транзакции и многое другое. Полное описание NPFS
выходит за пределы настоящей статьи.
В целом интерфейс NPFS весьма удобен для обмена
информацией между процессами, особенно в тех
случаях, когда процесс-клиент и процесс-сервер
выполняются на разных компьютерах одной
локальной сети. NPFS широко используется
операционной системой Windows NT для своих нужд. С
помощью NPFS решается множество задач, некоторые
из которых играют важную роль в обеспечении
безопасности операционной системы. Например,
каналы lsass, lsarpc и LANMAN используются при передаче по
сети имени и пароля пользователя при сквозной
аутентификации в домене Windows NT. Также стоит
отметить, что удаленный вызов процедур (RPC) в Windows
NT реализован как надстройка над NPFS.
Некоторые странности NPFS.
До тех пор, пока речь не идет о защите
информации, NPFS выглядит просто прекрасно. Но
стоит только начать исследовать этот интерфейс
на предмет устойчивости к злонамеренным
воздействиям прикладных программ,
обнаруживается множество удивительных фактов.
Пусть некий процесс создал экземпляр
некоторого канала. Другой процесс пытается
создать экземпляр того же самого канала, вызывая
CreateNamedPipe с тем же именем создаваемого канала. Как
это ни странно, второй экземпляр канала будет
успешно создан. Более того, если второй процесс
вызовет теперь ConnectNamedPipe, экземпляр канала,
созданный вторым процессом, сможет обслуживать
клиентов.
Итак, в системе появляются два объекта, имеющих
одинаковое имя, но обслуживаемых разными
процессами. Когда клиент подключается к каналу,
операционная система предоставляет ему первый
экземпляр, поскольку он был создан раньше. Но
когда к тому же каналу захочет подключиться
другой клиент, он будет подключен ко второму
экземпляру. При этом клиент никакими средствами
не сможет определить, какой процесс обслуживает
его запрос. Получается, что клиент хотел получить
услугу от одного процесса, а на самом деле эту
услугу ему предоставляет совсем другой процесс.
Это выглядит очень странно. Представьте себе, что
Вы открываете файл на жестком диске, а
операционная система лезет в Internet. При этом,
когда файл открыт, Вы никак не сможете узнать,
откуда был взят файл, который Вы открыли.
Особенно интересно, что процессы из
вышеприведенного примера могут быть запущены
разными пользователями. Например, пользователь
guest, локально работающий с контроллером домена,
может создать экземпляр канала lsass, и, дождавшись
подключения клиента, вмешаться в процедуру
сетевой аутентификации пользователя.
Другой интересный эффект. Каналы, как и любые
другие объекты Windows NT, могут иметь дескриптор
защиты (security descriptor), описывающий то, какие
пользователи имеют какие права на доступ к
данному объекту. Мне так и не удалось найти
простой метод, позволяющий получить дескриптор
защиты канала по его имени (конечно, можно это
сделать с помощью драйвера фиктивного
устройства, но это слишком трудоемко).
Эксперименты показывают, что любой канал,
даже системный, даже такой важный и
ответственный, как lsass, может быть открыт любым
пользователем. Все, что требуется от
пользователя — это аутентифицироваться под любым
именем на компьютере, на котором выполняется
процесс, обслуживающий данный канал.
Экспериментально установлено, что
пользователь guest с помощью обычной прикладной
программы может открывать системный канал lsass и
записывать туда все, что угодно. Сервер LSA
воспримет эту информацию и попытается ее
интерпретировать в терминах протоколов сетевой
аутентификации.
Поскольку средства манипулирования атрибутами
защиты каналов не только отсутствуют в
стандартных утилитах администрирования, но и не
описаны в документации MSDN, ни о какой регистрации
доступа пользователей к каналам не может быть и
речи — администратор просто не имеет средств,
позволяющих ему присваивать каналам системные
списки контроля доступа.
Из всего вышеперечисленного непосредственно
следует, что NPFS представляет собой прекрасный
объект для удаленных атак. Две возможные (но
далеко не единственно возможные) атаки
рассмотрены ниже.
Атака PipeBomb.
Что случится, если обычная прикладная
программа, запущенная обычным пользователем,
начнет в вечном цикле открывать на запись все
новые и новые экземпляры некоторого системного
канала и записывать в них огромные объемы
заведомо бессмысленной информации? Один из
возможных вариантов развития событий
заключается в том, что через короткий промежуток
времени все свободные экземпляры канала будут
заняты, и сетевая служба, использующая атакуемый
канал, будет отключена. Однако, как показывают
эксперименты, более вероятна другая картина.
Обычно, когда процесс-сервер понимает, что все
экземпляры его канала заняты, этот процесс
начинает создавать новые экземпляры канала.
Таким образом, чем больше экземпляров канала
открывает процесс-клиент, тем больше экземпляров
канала создает процесс-сервер. Как правило,
каждый экземпляр канала обслуживается отдельным
потоком (thread). Буферы для хранения информации,
передаваемой по каналу, занимают оперативную
память. По мере того, как процесс-клиент
открывает новые экземпляры канала,
процесс-сервер создает новые потоки. Когда
процесс-клиент записывает в открытые им
экземпляры канала бессмысленную информацию, эта
информация передается процессу-серверу и
засоряет его адресное пространство.
Все перечисленные эффекты приводят к тому, что
загрузка процессора компьютера, на котором
выполняется процесс-сервер, стабильно держится
на уровне 100% (при этом около 90% времени процессор
обслуживает процессы с базовым приоритетом High), а
объем свободной оперативной памяти этого
компьютера уменьшается со скоростью от 1 до 3
мегабайт в секунду. Когда и физическая, и
виртуальная памяти компьютера переполняются, и
начинается рост файла виртуальной памяти, эта
скорость несколько уменьшается. Уже через минуту
атакованный компьютер становится практически
неработоспособен (окно Explorer прорисовывается
несколько минут), а через 5-10 минут
перегруженность операционной системы достигает
такой степени, что команда Shutdown выполняется 3-6
часов.
Эта атака интересна тем, что использует не
слабости протокола TCP/IP, в общем-то, чуждого для
Windows NT, а слабости "родного" программного
обеспечения этой операционной системы. Атака
одинаково эффективно работает для любого
сервис-пака Windows NT 4.0, включая четвертый,
поражаются и рабочие станции, и серверы. Не
исключено, что эту атаку можно применять и в Internet,
инкапсулируя пакеты SMB в пакеты TCP/IP (сетевая
составляющая интерфейса NPFS организована как
надстройка над протоколом SMB).
Существует программа, реализующая данную
атаку.
Напоследок об авторских правах в отношении
данного параграфа. Первоначальная формулировка
идеи атаки PipeBomb принадлежит Петру Девянину,
детальная проработка идеи моя,
программированием занимался Сергей Заливакин.
Атака AdminTrap.
Как уже говорилось выше, прикладная программа,
выполняющаяся под управлением Windows NT с
полномочиями обычного пользователя, может
создать экземпляр канала, имеющий такое же имя,
как и один из системных каналов. При подключении
клиента к такому каналу клиент может
подключиться как к "родному" экземпляру,
обслуживаемому одним из системных процессов, так
и к "троянскому" экземпляру, созданному и
обслуживаемому прикладной программой.
В программном интерфейсе Win32 существует
функция ImpersonateNamedPipeClient, выполняющая олицетворение
(impersonation) клиента канала. Олицетворение
клиента канала заключается в том, что потоку,
вызвавшему данную функцию, назначается маркер
доступа (access token) клиента экземпляра канала, handle
серверного конца которого указан в качестве
параметра функции. При этом поток
процесса-сервера, обслуживающий данный
экземпляр канала, получает полномочия
пользователя, который подключился к этому
экземпляру канала в качестве клиента. Обычно
данная функция используется системными
процессами-серверами для временного понижения
полномочий, но если полномочия клиента превышают
полномочия сервера, наблюдается обратная
ситуация — после выполнения олицетворения
полномочия сервера повышаются. Для того чтобы
функция ImpersonateNamedPipeClient нормально выполнилась,
необходимо, чтобы маркер доступа клиентского
конца экземпляра канала представлял собой
маркер олицетворения (impersonation token). Если
процесс-клиент и процесс-сервер выполняются на
разных компьютерах сети, это всегда имеет место.
Никаких специальных привилегий для вызова
ImpersonateNamedPipeClient не требуется.
Если прикладная программа, выполняющаяся с
правами обычного пользователя, создаст
экземпляр канала, дождется подключения клиента и
выполнит олицетворение, эта программа получит
полномочия клиента, которые могут превышать
изначальные полномочия программы. Эта идея была
положена в основу атаки AdminTrap.
Программа AdminTrap создает троянский экземпляр
одного из системных каналов и ждет, когда к нему
подключится клиент. Затем AdminTrap выполняет
олицетворение клиента. Если олицетворение
прошло успешно, один из потоков программы AdminTrap
получает полномочия пользователя-клиента
троянского экземпляра канала. В Windows NT интерфейс
NPFS в основном используется для обмена
информацией между операционными системами
компьютеров локальной сети и удаленного
администрирования, прикладные программы
используют каналы редко. Поэтому вероятность
того, что программа AdminTrap после вызова
ImpersonateNamedPipeClient получит полномочия
администратора, весьма велика, а при наличии этих
полномочий программа может оказывать на
операционную систему локального компьютера
практически любые негативные воздействия.
Например, подобная программа может
использоваться для внедрения в систему
программных закладок.
Традиционно, демонстрационные версии программ,
предоставляющих обычному пользователю
полномочия администратора, запускают в
атакуемой системе командный интерпретатор,
выполняющийся с правами администратора. Для
описываемой атаки это невозможно, поскольку в
Windows NT при выполнении функции CreateProcess новому
процессу назначается маркер доступа
процесса-родителя, а не маркер доступа потока, из
которого была вызвана функция CreateProcess. Таким
образом, полномочия, полученные программой,
реализующей данную атаку, не могут быть
унаследованы порожденными ей процессами.
Поэтому для демонстрации того, что программа
AdminTrap действительно получает полномочия
пользователя, соединение с которым она
перехватила, была выбрана несколько
нестандартная процедура. Описываемая версия
программы, получив полномочия
пользователя-клиента, пытается зарегистрировать
в операционной системе
пользователя-администратора по имени AdminTrap. Для
предотвращения использования программы AdminTrap в
противозаконных целях в нее встроен ряд
блокировок, в том числе:
- пароль пользователя AdminTrap не описан ни в
настоящей статье, ни в файле ReadMe.txt, прилагаемом к
программе;
- пользователь AdminTrap создается заблокированным
(disabled). Снять эту блокировку может только
администратор;
- в качестве полного имени пользователь AdminTrap
получает информацию о том, кто, с какого
компьютера и в какое время провел атаку.
Таким образом, учетная запись пользователя
AdminTrap, созданная программой, не может
использоваться для несанкционированного
доступа к ресурсам операционной системы без
дополнительных действий, выполнить которые
может только администратор. Конечно, опытный
хакер легко сможет снять все перечисленные (и
неперечисленные) блокировки. Но, как показывает
опыт, опытные хакеры обычно не занимаются
подобной деятельностью. Как бы то ни было, не
нужно писать мне письма с просьбой отключить эти
блокировки — это бесполезно.
При программной реализации вышеописанной
атаки имела место одна техническая проблема.
Если к моменту создания троянского экземпляра
канала в системе существует N легальных
экземпляров этого канала, только N+1-й клиент
канала сможет подключиться к троянскому
экземпляру. Обычно системные процессы-серверы
создают от двух до десяти экземпляров каждого
обслуживаемого канала, поэтому, если не
предпринять специальных мер, ожидание
подключения клиента к троянскому экземпляру
канала будет довольно долгим. В программе AdminTrap
эта проблема решена следующим образом. Перед
созданием троянского экземпляра канала
программа подключается в качестве клиента ко
всем легальным экземплярам атакуемого канала.
Троянский экземпляр канала создается только
тогда, когда все легальные экземпляры канала
заняты и процесс-сервер, обслуживающий канал,
настолько перегружен, что не может создавать
новые экземпляры с той скоростью, с которой они
захватываются программой AdminTrap. Другими словами,
на операционную систему оказывается строго
дозированное воздействие PipeBomb. Необходимая
интенсивность воздействия определяется
программой самостоятельно путем анализа времени
реакции процесса-сервера на запросы клиентов.
Программа AdminTrap нормально функционирует под
управлением всех существующих на сегодняшний
день сервис-паков Windows NT 4.0. Мне удавалось
перехватывать следующие сетевые соединения:
- winreg — удаленное управление реестром, списком
сервисов, репликацией и административными
оповещениями (alerts); удаленный просмотр системных
журналов; удаленное диагностирование и оценка
производительности;
- spoolss — удаленное управление принтером.
Вероятно, существуют и другие сетевые
соединения, которые могут быть перехвачены
программой AdminTrap. Не исключено, что в некоторых
системах централизованного управления
безопасностью сети (System Management Server, Secret Net и т.д.) NPFS
используется системным программным
обеспечением для установки административных
соединений. При этом установка административных
соединений происходит без непосредственного
участия пользователя-администратора, что
упрощает перехват этих соединений — система
сканирует сеть каждые несколько минут и не нужно
ждать, пока администратор соизволит обратиться к
данному компьютеру сети.
Как с этим бороться.
Как было показано выше, в реализации интерфейса
NPFS в Windows NT имеются слабости, позволяющие
злоумышленникам осуществлять различные атаки
операционной системы. К этим слабостям относятся
следующие:
- Возможно создание двух и более объектов,
одноименных, но не идентичных функционально.
- Отсутствуют средства, позволяющие
определить, каким процессом обслуживается
экземпляр канала.
- Атрибуты защиты системных каналов,
принятые по умолчанию, предоставляют
пользователям права доступа, заведомо
превышающие потребности пользователей.
Рассмотрим несколько способов преодоления
вышеописанных слабостей.
Возможно создание двух и более объектов,
одноименных, но не идентичных функционально.
Фактически должно быть запрещено создание
процессом экземпляра канала, для которого уже
существует экземпляр, созданный другим
процессом. Это может быть сделано с помощью
драйвера фиктивного устройства, фильтрующего
информационные потоки, проходящие через драйвер
npfs.sys. Более простые способы решения данной
проблемы вряд ли приведут к успеху.
Отсутствуют средства, позволяющие
определить, каким процессом обслуживается
экземпляр канала.
Эта проблема также вряд ли может быть решена
без использования драйвера фиктивного
устройства. Этот драйвер должен перехватывать
системные вызовы CreateNamedPipe и сохранять в
специальном буфере информацию о том, каким
процессом обслуживается каждый существующий в
системе канал. Доступ к такому буферу может быть
реализован посредством стандартного интерфейса
управления устройствами.
Атрибуты защиты системных каналов, принятые
по умолчанию, предоставляют пользователям права
доступа, заведомо превышающие потребности
пользователей.
Стандартный метод решения подобных проблем
заключается в следующем. Создается сервис,
который автоматически загружается при старте
операционной системы, открывает все объекты,
защита которых по умолчанию
неудовлетворительна, и устанавливает этим
объектам новые атрибуты защиты. Подобный метод
описан, например, в [7], где он применяется для
изменения атрибутов защиты секций, в которых
хранится код часто используемых системных
библиотек (KnownDlls). К сожалению, для решения
проблемы с защитой каналов данный метод
неприменим.
Для того чтобы изменить атрибуты защиты
какого-либо объекта Windows NT, вначале надо открыть
этот объект с правом доступа WRITE_DAC (если нужно
получить старые атрибуты защиты объекта,
дополнительно потребуется право READ_CONTROL). Microsoft
рекомендует использовать для изменения
атрибутов защиты каналов функции GetFileSecurity и
SetFileSecurity (см. [5]), однако эти функции при попытке их
применения к каналам всегда сообщают об ошибке
ERROR_PIPE_BUSY.
Существует и обходной путь. В Windows NT имеются две
универсальные (и, как это обычно бывает,
недокументированные) функции, позволяющие
управлять защитой любых объектов. Это функции
NtQuerySecurityObject и NtSetSecurityObject. Функция NtQuerySecurityObject
позволяет получить атрибуты защиты любого
объекта, handle которого передан функции в качестве
параметра, функция NtSetSecurityObject — установить новые
атрибуты защиты любому объекту. Если получить
каким то образом handle экземпляра канала, открытый
с правами доступа READ_CONTROL и WRITE_DAC, то с помощью
двух вышеназванных функций можно установить
этому экземпляру канала любые атрибуты защиты.
Проблема заключается в том, чтобы открыть каждый
экземпляр каждого системного канала с
необходимыми правами доступа. Единственный
способ открытия уже существующего канала,
допустимый в Windows NT, заключается в подключении
клиента к одному из экземпляров канала. Однако,
как уже упоминалось, большинство системных
процессов-серверов Windows NT при подключении
клиентов к каналу создают новые экземпляры того
же канала. Получается, что чем больше объектов
получают корректные атрибуты защиты, тем больше
в системе создается объектов, имеющих
некорректные атрибуты защиты. Создается
ощущение, что Windows NT сознательно
противодействует попыткам усилить ее
защищенность.
Таким образом, попытки усилить защиту
системных каналов средствами программного
интерфейса Windows NT к успеху не приводят. Остаются
еще два универсальных метода, которые позволяют
изменить поведение операционной системы
практически произвольным образом. Это — запуск
системного отладчика и создание фиктивного
устройства. Согласно требованиям NCSC, запуск
системного отладчика в защищенной операционной
системе должен быть недопустим, поскольку
наличие в системе процесса, обладающего статусом
системного отладчика, само по себе представляет
угрозу безопасности операционной системы.
Остается только создание фиктивного устройства.
Драйвер такого устройства должен
перехватывать запросы драйверу npfs.sys на создание
нового экземпляра канала. После выполнения
запроса драйвер должен с помощью вызова
NtSetSecurityObject установить только что созданному
экземпляру канала корректные атрибуты защиты.
Получается, что решение вышеописанных проблем
не может быть достигнуто без создания вы системе
фиктивного устройства. Однако создание драйвера
фиктивного устройства, перехватывающего те или
иные системные вызовы, является весьма
трудоемкой задачей. Кроме того, драйверы,
перехватывающие системные вызовы kernel mode, как
правило, жестко привязаны к конкретной версии
ядра Windows NT. Установка нового сервис-пака чаще
всего делает такой драйвер неработоспособным. А
изготовлять для каждого сервис-пака свою версию
драйвера явно нецелесообразно.
Таким образом, полная ликвидация вышеописанных
слабостей NPFS "подручными средствами"
возможна, но вряд ли целесообразна. Скорее всего,
Microsoft выпустит заплату, решающую перечисленные
проблемы, раньше, чем увидит свет первая версия
драйвера, делающего то же самое. Впрочем,
программистам Microsoft придется изрядно
потрудиться. Фактически они должны полностью
переписать драйвер npfs.sys, что является весьма
непростой задачей, особенно если учесть
необходимость обеспечения обратной
совместимости со старыми спецификациями NPFS.
Все, что можно посоветовать пользователям и
администраторам — это ждать очередного патча Windows
NT и надеяться, что атаки злоумышленников,
построенные по вышеописанным схемам, обойдут их
стороной.
Литература.
- Х. Кастер. Основы Windows NT и NTFS. Microsoft Press. 1996.
- Коберниченко А.В. Недокументированные
возможности Windows NT. М. "Нолидж". 1998.
- Люцаров В.С., Ермаков К. В., Рудный Е. Б., Ермаков И.
В. Безопасность компьютерных сетей на основе Windows
NT. М.: Издательский отдел "Русская редакция"
ТОО "Channel Trading Ltd.", 1998.
- Проскурин В.Г. Программные закладки в
защищенных системах. // "Банковские
технологии". 6. 1998. с. 56-59.
- Microsoft Developer Network Library. Win32 Software Developer Kit. Win32 Programmer’s
Reference. Overviews. System Services. Security. Securable Objects.
|