У большинства людей программные файерволы ассоциируется с непроходимыми кирпичными стенами. Самоуверенные утверждения продавцов программных продуктов в области информационной безопасности только усиливают такое мнение. Они заявляют об автоматическом обеспечении полной безопасности посредством их "стен". Но что, если эти "стены" на самом деле сделаны из соломы?
Чтобы ответить на этот вопрос, нам нужно рассмотреть несколько основных понятий. Во-первых, что такое программный файервол? По сути, это ни что иное, как фильтр, который находится между вашими обычными приложениями и сетевыми компонентами операционной системы, и решает: что пропустить, а что нет. Для этого он помещается на ключевые позиции между приложениями и сетью и анализирует проходящую информацию в соответствии с некоторыми заданными правилами. Все, что разрешено, проходит дальше, а что не разрешено – отфильтровывается (возможно, с сохранением информации в протоколе, выдачей некоторого сообщения и т.д.).
Что это означает для устройства файервола? Какие критерии используются при анализе информации? Существуют две основные области, на которых файервол должен быть сфокусирован и из которых и следуют два основных компонента конструкции программного файервола.
Первая область находится на уровне пакетов (уровни 3 и 4 модели OSI). На этом уровне работа файервола заключается в поиске подозрительных или поврежденных пакетов, определении факта сканирования портов и оценке того, может или нет конкретный пакет быть помещен в стек протокола. Пакеты анализируются посредством сетевых критериев, например, формальная подлинность пакета, направление пересылки пакета (входящий/исходящий), адрес хоста-отправителя пакета и номер порта, адрес хоста-получателя пакета и номер порта, особые флаги пакета (это попытка установить соединение? Принадлежит ли этот пакет к уже установленному соединению?).
Другой компонент файервола работает на более высоком уровне, уровне процессов, проверяя, позволено ли процессу X установить соединение с данным хостом через данный порт, может ли этот процесс ждать соединения в данном диапазоне портов и т.д. В основном, файервол работает и как фильтр на уровне пакетов, и как фильтр на уровне процессов.
Как это делается? Конкретные детали в разных продуктах конечно различаются, но функционально, по сути, файерволы в основном одинаковы. Настоящий программный файервол (за исключением нескольких бесполезных программ, просто перехватывающих обращения к сокетам) использует два драйвера для непосредственного фильтрования: один для фильтра пакетов, другой для фильтра процессов. Есть еще и графический интерфейс, позволяющий пользователю менять настройки. Однако вся важная работа делается в ядре при помощи всего двух драйверов.
Фильтр пакетов
Что касается нижнего уровня, драйвера фильтрования пакетов, то он обычно реализуется одним из двух методов.
Первый метод подразумевает использование NDIS (Network Driver Interface Specification, Спецификация Интерфейса Сетевого Драйвера) – промежуточного драйвера. В основном, этот драйвер располагается между драйвером сетевой карты и драйверами протоколов (TCP/IP и т.д.). По существу, он является виртуальным адаптером, который выглядит для драйверов протоколов как драйвер сетевой карты, а для драйвера сетевой карты - как драйвер протокола.
Другой общий метод реализации фильтра пакетов - подмена подмножества библиотечных фунцкий NDIS, используемых драйверами протоколов. Это означает, что фильтр пакетов вставляется в обертку драйвера NDIS, располагаясь прямо между драйверами протоколов и всем, что находится на более низком уровне. Хотя это и совсем другая реализация фильтра, функциональна она схожа с методом, описанным выше.
Работа, выполняемая драйвером фильтрования пакетов, довольна проста. Драйвер анализирует каждый пакет, который видит, в соответствии с критериями, определенными в правилах файервола. Он отслеживает такие параметры пакета, как исходные и целевые хосты и порты, уровень фрагментации, тип протокола, флаги, принадлежит ли пакет уже открытому соединению и т.д. Например, если используется протокол TCP и у пакета установлен флаг SYN (попытка установления соединения), фильтр будет искать правило, согласно, которому можно или нельзя устанавливать соединение между исходным и целевым хостом по заданным портам. Если установка соединения разрешена, фильтр добавит это соединение к внутреннему списку открытых соединений. Таким образом файервол хранит перечень открытых соединений, формируя базу проверки пакетов. Если пакет удовлетворяет правилам или принадлежит списку открытых соединений, то он передается на следующий уровень - или драйверам протоколов (если пакет входящий), или драйверам более низкого уровня (если пакет исходящий). Если пакет заблокирован правилами, то он тихо отбрасывается. Такой пакет не передается на следующий уровень, и, следовательно, никто о нем ничего так и не узнает. Возможно также, что ожидающему потоку будет передана информация для последующего протоколирования или показа предупреждающего сообщения.
Фильтр процессов
Существует также и другой драйвер в конструкции программного файервола, на что имеются серьезные причины. На уровнях, с которыми мы имели дело до сих пор, не было информации о процессах. Все сводилось к пакетам, которые пересылаются туда и обратно. Однако понять логику этих пересылок можно только на уровне приложений. Таким образом, необходим еще один, более высокий, уровень фильтрования. В основном, все делается в пространстве ядра, путем обертывания (перехвата вызовов) тех функций TDI (Transport Driver Interface, Интерфейс Транспортных Драйверов), которые используются приложением и/или вспомогательной библиотекой (например, WinSock) для передачи данных драйверам транспортного протокола и обратно.
Можно применять и альтернативные методы. Например, WinSock API (набор функций, который используют подавляющее большинство приложений для доступа к сети) построен на многослойной модели, позволяющей сторонним разработчикам вставлять свои расширения между интерфейсом приложения и основным сетевым протоколом. Такое расширение может быть добавлено посредством реализации LSP (Layered Service Provider, Провайдер Службы Слоев) и вставкой его в цепь LSP. LSP - это стандартная библиотека Windows, которая соответствует определенным конструкторским спецификациям и имеет специальную функцию добавления в цепочку протокола WinSock. В модели WinSock все сетевые данные проходят через эту цепочку, и каждый LSP ответственен за передачу данных к следующему провайдеру сверху или снизу (зависит от направления движения данных). Помимо просто передачи LSP может обрабатывать или изменять данные в соответсвии со своей собственной функцией. Фильтр процессов файервола может быть реализован в виде LSP, находясь в цепочке протокола и выборочно пропуская данные или тихо отбрасывая их в зависимости от собственных критериев.
Метод LSP, однако, не является наиболее совершенным решением задачи фильтрования процессов, поскольку он предполагает, что приложение использует WinSock. Для обхода такого фильтра вредоносному приложению достаточно использовать собственный драйвер для взаимодействия непосредственно с драйвером протокола через TDI, соответственно обходя WinSock. Первый описанный метод обертывания функций TDI является лучшей альтернативой, поскольку работает на более низком уровне.
Задачей фильтра процессов является анализ попыток передать/получить данные работающими приложениями. Фильтр отслеживает идентификатор процесса, пытающегося передать или принять данные, и анализирует его характеристики в соответствии с набором правил. Решающим, конечно же, является вопрос: "Может ли выполняемый файл, породивший этот процесс, выполнить действие, которое он пытается выполнить?" Смысл заключается в том, чтобы найти файл по идентификатору процесса, возможно, подсчитать хэш для проверки целостности файла, найти файл в наборе правил и, наконец, проверить, позволено ли этому файлу выполнить заданное действие или нет.
Проблемы фильтрования процессов
С такой реализацией, однако, связаны некоторые неприятности. Например, было бы желательно проверять, использует ли процесс какие-либо либо присоединенные модули (DLL), и, если да, должны ли эти модули им использоваться. Смысл в том, что код, содержащийся в присоединенных модулях, будет исполняться в контексте того процесса, к которому они присоединены. Поэтому, если "плохой" процесс присоединит модуль EVIL.DLL к "хорошему" процессу X, то "хороший" процесс X выполнит код из модуля EVIL.DLL как свой собственный. Решением является учет всех загруженных модулей, нахождение файлов, содержащих их образы (полный путь и имя), и их проверка на соответствие с таблицей модулей, которым позволено присоединяться к процессу. Файерволы часто осуществляют такие проверки и спрашивают, позволить ли присоединение такого-то модуля. Файервол не может точно знать все возможные нынешние и будущие DLL, хорошие и плохие, которые могут существовать. Поэтому решение о том, хорошим или плохим является тот или иной модуль, принимает пользователь. Другая неприятность заключается в том, что код процесса может быть изменен в памяти (один процесс может модифицировать код в памяти другого процесса для изменения его поведения). Или что процесс был запущен вредоносной программой (что также позволяет вредоносной программе изменить поведение процесса). Список можно продолжать и дальше.
Только с некоторыми из этих проблем современные файерволы адекватно справляются. Дело в том, что для вредоносных программ существует слишком большое количество способов замаскироваться, чтобы обойти фильтрацию процессов программного файервола - от простых до очень сложных. "Плохой" процесс может изменить хороший процесс в памяти. Он может использовать преимущества хороших процессов, например, запустить браузер с командной строкой, указывающей последнему загрузить заданную вредоносную страницу. Даже при возможности фильтра отслеживать ход порождения процессов, множественные уровни непрямых вызовов могут его запутать ("плохой" процесс может запустить cmd.exe с командной строкой, указывающей ему запустить еще один cmd.exe, который уже потом запустит браузер с означенной страницей). Или вредоносное приложение может применить против файервола агрессивную тактику, сделав его недееспособным, подделывая щелчки мыши при попытке отреагировать на предупреждение... Помните, все, что может сделать пользователь, может за него сделать и программа (в этом контексте, разумеется).
Все, сказанное выше, относилось к обходу фильтра процессов. Но перед атакующим открывается целый мир новых возможностей, когда он осознает, что ему даже и не надо обходить фильтр процессов. Помните, ниже фильтра процессов, информация о процессах теряется. Информация становится просто набором данных, идущих к машине и от нее, и именно так она предстает перед следующим фильтром - фильтром пакетов. Все, что нужно сделать атакующему, это использовать драйвер для вставки пакетов ниже уровня, на котором находится фильтр процессов, и ему остается только позаботиться о фильтре пакетов. Если атакующий использует уже разрешенный порт (скажем, порт номер 80, 25 или какой-нибудь еще), фильтр пакетов спокойно позволит ему провести свои данные. И это даже не упоминая о возможности просто пройти ниже уровня фильтра пакетов.
Обход файервола: использование LSP трояна через порт 80
Давайте рассмотрим один из случаев обхода файервола. Включив вредоносный провайдер LSP в стек протоколов, вредоносное приложение может стать непосредственно частью стека протоколов, получив возможность использовать нормальные соединения, сделанные нормальными процессами, при этом подменяя их входные и выходные данные. Что может быть лучше для атакующего, чем посылать команды своему трояну и получать результаты их выполнения путем открытия разрешенного соединения со, скажем, нормальным HTTP сервером, запущенным на целевой машине.
Все, что должен сделать атакующий, это создать специальный LSP и вставить его в цепочку протоколов WinSock. Также как и любой другой LSP, вредоносный провайдер получит возможность отслеживать весь входящий и исходящий сетевой трафик и изменять его в своих собственных целях, прежде чем передавать дальше, или вовсе никуда не передавать. Что самое интересное, этот провайдер может порождать ложный трафик и передавать его дальше как нормально полученный. Таким образом, LSP является прекрасным местом для атак типа "Посредник" (man-in-the-middle).
Рассмотрим следующий пример. Имеется корпоративная сеть, тщательно защищенная специальным шлюзом, которая предоставляет общий HTTP сервер, видимый из интернета. Сервер снабжен всеми необходимыми средствами защиты; на нем запущен файервол, который закрыл все порты кроме 80-го. Только процессу HTTP сервера дозволено работать с этим портом. Теперь, скажем, эта машина оказалась зараженной специально сделанным LSP трояном, например, Trojan.Riler.D или Daqa.A, который перехватывает взаимодействия через порт 80. Сможет ли атакующий из интернета удачно взаимодействовать с этим трояном в обоих направлениях?
Ответ: скорее всего да. Как? Используя обычный HTTP запрос, посланный на сервер извне. Помните, сетевые данные просматриваются на сервере LSP трояном. Теперь атакующий присоединится к HTTP серверу и пошлет несколько обычных запросов, смешанных с закодированным запросом к трояну (например, строка "GET /index.htm HTTP%%givemeadmin%%/1.1"). Шлюз перенаправит запрос серверу, поскольку это нормальный запрос, полученный через порт 80. На сервере фильтр пакетов локального файервола увидит обычный пакет, принадлежащий установленному соединению, и пропустит его. На уровне TDI фильтр процессов увидит, что пришел пакет через допустимый порт допустимому процессу (HTTP сервер), поэтому тоже его пропустит. Внутри стека протоколов LSP троян просмотрит данные и распознает "%%givemeadmin%%" как команду для выполнения. Затем он удалит эту часть из пакета и передаст дальше обычный HTTP запрос. А тем временем сделает то, что от него требовала эта команда (возможно, начнет взламывать хэши паролей, хранимые на этом компьютере).
Когда пакет дойдет до сервера, тот увидит нормальный запрос: "GET /index.htm HTTP/1.1". Сервер пошлет в ответ содержимое файла index.htm или сообщение об ошибке. Предположим, сервер ответит "HTTP/1.X 404 Not Found". Этот ответ на обратном пути опять будет перехвачен LSP трояном. Троян, зная, что связь идет через вредоносное соединение, использует возможность послать любую информацию (например, пароль администратора, как только последний будет взломан). Троян вставит закодированный ответ в сообщение сервера и передаст его дальше. Другими словами ответ теперь будет выглядеть так: "HTTP/1.X %%thepasswordisGOD%%404 Not Found". Для локального файервола все это будет выглядеть так, как будто нормальный процесс послал данные через разрешенный порт, поэтому файервол пропустит этот пакет, так же как и шлюз. Что в результате? Один счастливый злоумышленник, одна скомпрометированная сеть.
Естественно, это просто небольшой пример. Более сложный пример, включающий подмену бинарного протокола (FTP или HTTPS) вкупе с кодированием данных трояна, сделает обнаружение вторжения невероятно сложным.
Наконец, хотя возможности атаки, изображенные здесь, кажутся абстрактными, на самом деле они весьма практические и становятся реальными в руках умелого взломщика. Допустимый размер статьи не позволяет привести подробное обсуждение примеров, но должно быть понятно, что при таком типе взаимодействия ни один продукт не может предложить абсолютной защиты. Не существует никакого волшебного решения проблемы. Файерволы и антивирусные приложения - всего лишь инструменты для закрытия очень маленького участка очень большой дыры. Особенно в корпоративном случае, когда мотивация атакующего достаточно велика.
Кто-то может сказать, что есть еще и системы обнаружения вторжений (IDS). Как быть с ними? Проблема в том, что многие другие возможности защиты редко применяются. Более того, даже когда эти средства защиты применяются, велика вероятность того, что человек, получающий тревожные сообщения от IDS, не имеет надлежащей подготовки или знаний, чтобы понять информацию, которую видит. В некоторых случаях большие сети получают сотни тысяч тревожных сообщений каждый день.
Проблема систем обнаружения вторжений состоит в том, что какая-то активность остается не обнаруженной, какая-то - неправильно интерпретируемой. Очень много корпораций инвестируют в технологии, но не инвестируют в развитие людей, которые будут использовать и управлять оборудованием.
Тогда возникает вопрос, есть ли способ надежного обнаружения взаимодействия хакера и компрометируемого компьютера? К сожалению, ответ: нет. Хакер может использовать разрешенный сеанс связи для передачи команд и получения результатов их выполнения. Проделывая это все, хакер аккуратно обходит все возможности файервола по фильтрованию. Обычные методы обнаружения в этом случае бесполезны. Причем не только методы файервола, но и методы всей IDS. В идеале атакующий проделывает некоторую нехитрую перекодировку своих команд так, что даже просмотр отдельных пакетов не гарантирует немедленного обнаружения вторжения.
Обнаружение атаки
Теперь, зная некоторые методы обхода систем безопасности, можно задать вопрос: как все-таки можно обнаружить такое вторжение? На этот вопрос, к сожалению, не существует определенного ответа.
Если атакующий использует известный LSP троян, то его можно легко обнаружить локальным антивирусным сканером, предполагая, разумеется, что сигнатура этого трояна имеется в базе данных нашего сканера. Однако в большинстве ситуаций использование известного LSP трояна опытным хакером является скорее исключением. Опытный атакующий для своих целей, скорее всего, создаст свой LSP троян, или изменит существующий таким образом, чтобы его сигнатура не определялась текущими версиями антивирусных сканеров.
К тому же, достаточно опытному хакеру вполне по силам написать троян, который очень сложно, практически невозможно, обнаружить. Используя современные методы, можно создать исполняемый код без каких бы то ни было однозначно определяемых сигнатур. Таким образом, квалифицированный хакер может заставить компании-разработчики антивирусного ПО сильно потрудиться для создания средств обнаружения своих троянов, даже если эти компании имеют на руках копии вредоносного кода, что маловероятно, поскольку злоумышленник, атакующий корпоративные сети, обычно создает свои инструменты отдельно для каждой конкретной сети и не выкладывает их для общего пользования.
Вернемся к вопросу определения вторжения. Факты таковы, что если достаточно талантливый злоумышленник атакует конкретную компанию, по существу, эта компания не сможет обнаружить вторжение.
В этой статье освещается компромисс между безопасностью сети и ее простотой использования. При наличии достаточных средств одним из возможных вариантов является написание дополнительных IDS сигнатур для учета значительного количества способов обхода высокоуровневого фильтра. Это, правда, потребует значительных затрат времени и знаний. Не каждая организация может себе это позволить, однако всегда можно нанять или подготовить специалиста в области безопасности. Вашей организации надо иметь не только стандартный файервол и IDS, но и распространить систему безопасности на другие уровни. Многообещающей технологией является система предотвращения вторжений (intrusion prevention system, IPS). Эта и несколько других методик, основанных на статистическом анализе обмена пакетами, может наметить положительные сдвиги в таких проблемах, как обнаружение LSP троянов.
Заключение
Не существует совершенного решения проблемы обеспечения безопасности. Атака уровня LSP – это всего лишь один пример, тогда как существуют еще много других изощренных способов. Все, что могут сделать организации с хорошим бюджетом, это продолжать внедрять средства и методы обеспечения безопасности на разные уровни своей сети. Ну и, как всегда, администраторы и простые пользователи должны поддерживать в надлежащем состоянии свой образовательный уровень в отношение обеспечения безопасности. Файервол не такой уж и неуязвимый, как многие думают.