Руткиты. Опасность нового уровня
Кажется, еще совсем недавно в сводках антивирусных лабораторий появились первые упоминания о вредоносных программах, которые призваны скрывать присутствие своих собратьев в системе. Но не мне вам объяснять, какими темпами идет развитие мира электронов и байтов, а посему руткитами (rootkits) уже сложно удивить даже рядового пользователя — чего уж говорить о профессионалах в сфере безопасности. Руткиты прочно вошли в мир андеграунда и теперь стали одной из первоочередных головных болей для производителей антивирусных систем, т.к. сигнатурным сканированием, эвристическим анализатором и проактивной защитой их трудно достать. В сегодняшней статье я бы хотел вспомнить, с чего это начиналось, какие технологии активно использовались на заре и каким образом security experts справлялись с напастью.
Как обычно, статью такого рода я начну с более детального объяснения на тему, "что есть руткит". Для этого прибегну к определению из Википедии, ну и добавлю свой комментарий.
Rootkit (руткит, от англ. root kit, то есть "набор root'а") — программа или набор программ для скрытия следов присутствия злоумышленника или вредоносной программы в системе.
Термин, как многие догадались по первой части слова — "root", пришел к нам из Unix-подобных операционных систем. Собственно, именно на этих системах и начали свою работу подобные программы. Задачей руткита является, как уже было сказано, сокрытие присутствия вредоносного ПО на компьютере-жертве. На данный момент руткиты используют множество технологий: перехват функций winAPI (API hooking) на уровне пользователя, перехват функций на уровне ядра, изменение MBR и т.д. Перечислена только малая часть, да и то относящаяся исключительно к Windows-подобным системам, а если говорить в общем, то список получится более чем внушительный и ужасающий:). Ну что ж, давайте приступим к рассмотрению классификации руткитов, которые существовали на заре своего же развития, технологий, которые ими использовались, и способы, которыми защищалась ИТ-общественность.
Например…
Сейчас я попробую объяснить принцип работы руткита на примере программы — менеджера системных процессов tasklist. Допустим, решили вы просмотреть активные процессы. Запускаем программу, при этом в память загружается код программы и необходимые ей функции из сисбиблиотек. После этого программа запрашивает функцию winAPI NtQuerySystemInformation, которая находится в ntdll.dll. Эта функция ответственна за формирование и выдачу списка процессов. На самом деле эта функция просто вызывает другую — функцию уровня ядра (ZwQuerySystemInformation) — посредством прерывания int 0x2E. Последняя же напрямую связывается с памятью ядра и запрашивает PsActiveProcessList, который мы и получаем в итоге. Так вот, руткит может вставить свои пять копеек на любом из этих этапов. На рис. 1 вы сможете увидеть результат работы утилиты tasklist при работающем рутките Hacker Defender. Раньше была популярна идея замены системных файлов и утилит — например, net share или netstat, — однако она довольно быстро ушла с подиума, т.к. реализация ее была не так продуктивна, как этого ожидали вначале.
Классификация
Руткиты в большинстве своем занимаются заменой или модификацией данных и программного кода. Естественно, подобные вещи заставляют их работать с разными областями памяти и на разных уровнях доступа, именно по этому признаку — уровень привилегий — они и делятся на две группы: уровень пользователя (user level) и уровень ядра системы (kernel level). Кроме того, существует еще и разделение по методу реализации — тут мы имеем также две группы: манипуляция объектами ядра (direct kernel object manipulation) и модификация пути исполнения (modify execution path) (рис. 2). В большинстве случаев встречаются руткиты уровня пользователя, которые используют метод изменения пути исполнения.
Методы
Теперь хочу коротко остановиться на методах реализации, которые дали жизнь руткитам как зловредам и определили их в отдельный класс. Пожалуй, самым популярным тогда был метод перехвата и изменения вызовов winAPI-функций. Самое интересное заключается в том, что изначально это была совсем не вредоносная технология. Она использовалась для проверки и перепрофилирования процессов, собственно, внедрение dll тоже использовалось изначально в этих целях. Однако, как обычно, нашелся кто-то, кому пришла в голову мысль использовать сии вещи не во благо. Работу метода я опишу, опираясь на руткит Hacker Defender. Все сводилось к тому, что руткит должен был заменить первые байты кода функции, вследствие чего выполнялся совсем другой код, который был предварительно загружен в адресное пространство программы. Чтобы выполнить такой алгоритм, необходимо сделать следующее: для начала узнается адрес функции и в адресное пространство программы копируется код псевдофункции. Таким образом, когда пользовательская программа вызывает функцию API, например, NtQuerySystemInformation, вызов передается функции предобработки данных, которая затем может вызвать исходную функцию, которая, в свою очередь, вернет результаты функции постобработки. Функция постобработки модифицирует данные, которые вернула исходная функция, например, удаляя некоторые записи (рис. 3). После этого программы, которые будут использовать функцию, получат информацию о системе, отличную от реальной.
Такого рода операцию можно провернуть и на уровне ядра. При переходе нулевого уровня пользовательские функции вызывают прерывание 0x2E, которому в качестве параметра задается адрес функции, но уже низкоуровневой, которая может работать с ядром. Адреса низкоуровневых функций хранятся в специальной таблице — System Services Dispatch Table (SSDT). В таком случае руткиты модифицируют адреса, находящиеся в SSDT, тем самым вместо стандартной функции вызывается ложная. Это проворачивается посредством драйвера или приложения user-land, которое может управлять устройством dev\physicalmemory. Естественно, это возможно при наличии определенных прав, ну, или бреши в системе. Изменению могут подлежать также и данные, так что изменение функций — вариант не единственный. Например, достаточно известный руткит FU действует следующим образом: он модифицирует список PsActiveProcessList, в котором содержится информация об активных процессах — из этого списка информацию получает
ZwQuerySystemInformation. При этом процесс остается существовать в качестве "свободного" потока и будет нормально функционировать, поскольку распределение процессорного времени в Windows основано на потоках, а не на процессах. Список PsActiveProcessList содержит набор структур EPROCESS, каждая из которых, кроме информации о процессе, содержит ссылки на предыдущий и последующие процессы в списке (ActiveProcessLinks). Программа, реализующая DKOM, изменяет значение ActiveProcessLinks предыдущего и последующего процесса в списке так, чтобы перечисление шло в обход скрываемой программы (рис. 4).
Защита
Большинство антивирусов последних версий уже имеют достаточно сильный алгоритм борьбы с руткитами, чего нельзя было сказать во времена "первых ласточек". Тогда не то что антивирусные системы — даже межсетевые экраны не могли обнаружить "нелегальные" соединения. Поэтому все надежды были направлены на специализированные программы, которые создавались хак-сообществами, отдельными хакерами и, конечно, спецами по ИБ. Это, безусловно, давало свои плоды, т.к. программы писались для результата. Блокировать соединения, замаскированные руткитами, можно было и с помощью файрволлов, которые пользовались сценариями. Вот пример кода, который мог предотвратить открытие соединения руткитом Hacker Defender. Сценарий подходит для программы обнаружения вторжений NIDS Snort (поклонники никсов сразу вспомнят свинку вопреки касперскому=)):
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"Rootkit Hacker Defender 1.0 connection attempt"; flow:to_server,established; content:"|01 9a 8c 66 af c0 4a 11 9e 3f 40 88 12 2c 3a 4a 84 65 38 b0 b4 08 0b af db ce 02 94 34 5f 22 00|"; rev:1;)
Возможно также обнаружить руткит с помощью утилиты RKDetect — вся ее соль заключается в использовании "аномального подхода" при получении списка процессов. Список системных служб запрашивается через WMI, а затем службы перечисляются с помощью Service Manager (SC). Эти списки различаются, поскольку WMI работает на уровне пользователя и подвержен влиянию rootkit, а SC — нет (рис. 5).
наКонец
Вот, собственно, и все, что я хотел рассказать. Конечно, это далеко не все, а совсем даже чуть-чуть. Появление руткитов повлекло за собой большие перемены — даже переориентация вирусописателей с классики на сетевых червей выглядела намного менее ярко, нежели выход принципиально новой угрозы — руткитов. В конце концов, сокрытие следов на таком низком уровне, который могут предоставить последние, очень сложно определить, а значит, и заметить зловреда на порядок сложнее. А если учесть, что злоумышленник всегда на шаг впереди системы безопасности, то картина получается и вовсе не из приятных. Поэтому глядите в оба и будьте начеку.
Евгений Кучук, q@sa-sec.org
Как обычно, статью такого рода я начну с более детального объяснения на тему, "что есть руткит". Для этого прибегну к определению из Википедии, ну и добавлю свой комментарий.
Rootkit (руткит, от англ. root kit, то есть "набор root'а") — программа или набор программ для скрытия следов присутствия злоумышленника или вредоносной программы в системе.
Термин, как многие догадались по первой части слова — "root", пришел к нам из Unix-подобных операционных систем. Собственно, именно на этих системах и начали свою работу подобные программы. Задачей руткита является, как уже было сказано, сокрытие присутствия вредоносного ПО на компьютере-жертве. На данный момент руткиты используют множество технологий: перехват функций winAPI (API hooking) на уровне пользователя, перехват функций на уровне ядра, изменение MBR и т.д. Перечислена только малая часть, да и то относящаяся исключительно к Windows-подобным системам, а если говорить в общем, то список получится более чем внушительный и ужасающий:). Ну что ж, давайте приступим к рассмотрению классификации руткитов, которые существовали на заре своего же развития, технологий, которые ими использовались, и способы, которыми защищалась ИТ-общественность.
Например…
Сейчас я попробую объяснить принцип работы руткита на примере программы — менеджера системных процессов tasklist. Допустим, решили вы просмотреть активные процессы. Запускаем программу, при этом в память загружается код программы и необходимые ей функции из сисбиблиотек. После этого программа запрашивает функцию winAPI NtQuerySystemInformation, которая находится в ntdll.dll. Эта функция ответственна за формирование и выдачу списка процессов. На самом деле эта функция просто вызывает другую — функцию уровня ядра (ZwQuerySystemInformation) — посредством прерывания int 0x2E. Последняя же напрямую связывается с памятью ядра и запрашивает PsActiveProcessList, который мы и получаем в итоге. Так вот, руткит может вставить свои пять копеек на любом из этих этапов. На рис. 1 вы сможете увидеть результат работы утилиты tasklist при работающем рутките Hacker Defender. Раньше была популярна идея замены системных файлов и утилит — например, net share или netstat, — однако она довольно быстро ушла с подиума, т.к. реализация ее была не так продуктивна, как этого ожидали вначале.
Классификация
Руткиты в большинстве своем занимаются заменой или модификацией данных и программного кода. Естественно, подобные вещи заставляют их работать с разными областями памяти и на разных уровнях доступа, именно по этому признаку — уровень привилегий — они и делятся на две группы: уровень пользователя (user level) и уровень ядра системы (kernel level). Кроме того, существует еще и разделение по методу реализации — тут мы имеем также две группы: манипуляция объектами ядра (direct kernel object manipulation) и модификация пути исполнения (modify execution path) (рис. 2). В большинстве случаев встречаются руткиты уровня пользователя, которые используют метод изменения пути исполнения.
Методы
Теперь хочу коротко остановиться на методах реализации, которые дали жизнь руткитам как зловредам и определили их в отдельный класс. Пожалуй, самым популярным тогда был метод перехвата и изменения вызовов winAPI-функций. Самое интересное заключается в том, что изначально это была совсем не вредоносная технология. Она использовалась для проверки и перепрофилирования процессов, собственно, внедрение dll тоже использовалось изначально в этих целях. Однако, как обычно, нашелся кто-то, кому пришла в голову мысль использовать сии вещи не во благо. Работу метода я опишу, опираясь на руткит Hacker Defender. Все сводилось к тому, что руткит должен был заменить первые байты кода функции, вследствие чего выполнялся совсем другой код, который был предварительно загружен в адресное пространство программы. Чтобы выполнить такой алгоритм, необходимо сделать следующее: для начала узнается адрес функции и в адресное пространство программы копируется код псевдофункции. Таким образом, когда пользовательская программа вызывает функцию API, например, NtQuerySystemInformation, вызов передается функции предобработки данных, которая затем может вызвать исходную функцию, которая, в свою очередь, вернет результаты функции постобработки. Функция постобработки модифицирует данные, которые вернула исходная функция, например, удаляя некоторые записи (рис. 3). После этого программы, которые будут использовать функцию, получат информацию о системе, отличную от реальной.
Такого рода операцию можно провернуть и на уровне ядра. При переходе нулевого уровня пользовательские функции вызывают прерывание 0x2E, которому в качестве параметра задается адрес функции, но уже низкоуровневой, которая может работать с ядром. Адреса низкоуровневых функций хранятся в специальной таблице — System Services Dispatch Table (SSDT). В таком случае руткиты модифицируют адреса, находящиеся в SSDT, тем самым вместо стандартной функции вызывается ложная. Это проворачивается посредством драйвера или приложения user-land, которое может управлять устройством dev\physicalmemory. Естественно, это возможно при наличии определенных прав, ну, или бреши в системе. Изменению могут подлежать также и данные, так что изменение функций — вариант не единственный. Например, достаточно известный руткит FU действует следующим образом: он модифицирует список PsActiveProcessList, в котором содержится информация об активных процессах — из этого списка информацию получает
ZwQuerySystemInformation. При этом процесс остается существовать в качестве "свободного" потока и будет нормально функционировать, поскольку распределение процессорного времени в Windows основано на потоках, а не на процессах. Список PsActiveProcessList содержит набор структур EPROCESS, каждая из которых, кроме информации о процессе, содержит ссылки на предыдущий и последующие процессы в списке (ActiveProcessLinks). Программа, реализующая DKOM, изменяет значение ActiveProcessLinks предыдущего и последующего процесса в списке так, чтобы перечисление шло в обход скрываемой программы (рис. 4).
Защита
Большинство антивирусов последних версий уже имеют достаточно сильный алгоритм борьбы с руткитами, чего нельзя было сказать во времена "первых ласточек". Тогда не то что антивирусные системы — даже межсетевые экраны не могли обнаружить "нелегальные" соединения. Поэтому все надежды были направлены на специализированные программы, которые создавались хак-сообществами, отдельными хакерами и, конечно, спецами по ИБ. Это, безусловно, давало свои плоды, т.к. программы писались для результата. Блокировать соединения, замаскированные руткитами, можно было и с помощью файрволлов, которые пользовались сценариями. Вот пример кода, который мог предотвратить открытие соединения руткитом Hacker Defender. Сценарий подходит для программы обнаружения вторжений NIDS Snort (поклонники никсов сразу вспомнят свинку вопреки касперскому=)):
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"Rootkit Hacker Defender 1.0 connection attempt"; flow:to_server,established; content:"|01 9a 8c 66 af c0 4a 11 9e 3f 40 88 12 2c 3a 4a 84 65 38 b0 b4 08 0b af db ce 02 94 34 5f 22 00|"; rev:1;)
Возможно также обнаружить руткит с помощью утилиты RKDetect — вся ее соль заключается в использовании "аномального подхода" при получении списка процессов. Список системных служб запрашивается через WMI, а затем службы перечисляются с помощью Service Manager (SC). Эти списки различаются, поскольку WMI работает на уровне пользователя и подвержен влиянию rootkit, а SC — нет (рис. 5).
наКонец
Вот, собственно, и все, что я хотел рассказать. Конечно, это далеко не все, а совсем даже чуть-чуть. Появление руткитов повлекло за собой большие перемены — даже переориентация вирусописателей с классики на сетевых червей выглядела намного менее ярко, нежели выход принципиально новой угрозы — руткитов. В конце концов, сокрытие следов на таком низком уровне, который могут предоставить последние, очень сложно определить, а значит, и заметить зловреда на порядок сложнее. А если учесть, что злоумышленник всегда на шаг впереди системы безопасности, то картина получается и вовсе не из приятных. Поэтому глядите в оба и будьте начеку.
Евгений Кучук, q@sa-sec.org
Компьютерная газета. Статья была опубликована в номере 47 за 2008 год в рубрике безопасность