Анализ инцидента на работающей Linux системе.
Мариус Бурдах, перевод - Saint_I, оригинал
Часть первая
1. Введение
Во время расследования инцидента мы часто сталкиваемся с
ситуацией, когда пользователь или администратор скомпрометированной системы
не выключил питание компьютера. В этом случае мы можем получить доступ к ценной
информации, которая будет утеряна при выключении компьютера. Речь идет о
запущенных процессах, открытых TCP/UDP портах, образах удаленных программ,
которые всё ещё находятся в основной памяти, содержимом буферов, очередях
запросов на соединение, установленных соединениях и модулях, загруженных в
зарезервированную часть виртуальной памяти ядра ОС Linux. Вся эта информация
может помочь исследователю в расследовании инцидента. Кроме того, если инцидент
произошел сравнительно недавно, возможно восстановить практически всю информацию
о данных используемых взломщиком и о предпринятых им действиях.
Иногда описанные здесь процедуры – единственный способ получения
данных, так как определённые типы злонамеренного кода, такие - как LKM руткиты,
загружаются только в память и не вносят никаких изменений в файлы или каталоги.
Подобная ситуация существует в операционной системе Windows, например, червь
Witty, код которого нигде не сохраняется в виде файла, но тем не менее этот
червь заразил систему и выполнил произвольный код в памяти системы.
С другой стороны методы, представленные ниже, также имеют
серьёзные ограничения и нарушают первоначальные требования сбора процедур
для цифрового исследования – требования, которые не могут быть легко выполнены.
Например, утилита, предназначенная для сбора информации пространства ядра,
полученного в результате изменений состояния целевой системы. Запуская любую
утилиту на работающей системе, мы загружаем её в память и создаём минимум
один процесс, который может перезаписать возможную улику. Создавая новый процесс,
система управления памятью операционной системы размещает данные в основной
памяти и тем самым может переписать другие, не освобождённые данные из основной
памяти или на swap разделе.
Также проблемы возникают, когда мы планируем действовать
в рамках законодательства. Подписи взломщика, найденные в образах основной
памяти не могут быть доверенным источником, так как могут быть созданы с помощью
утилит. Итак, до того, как предпринять какие-либо действия, мы должны решить,
необходимо ли извлекать данные с работающей скомпрометированной системы или
нет. Зачастую сбор информации не стоит затраченного времени. В основной памяти
мы можем найти пароли или зашифрованные файлы. Используя файловую систему
/proc мы можем так же восстановить программы, которые были удалены, но всё
ещё расположены в памяти.
В идеале я мог бы себе представить тип аппаратно-ориентированного
решения для Intel компьютеров, которое бы позволяло нам полную выгрузку памяти
на внешний носитель без какой-либо помощи операционной системы. Подобное решение
существует на компьютерах Sparc, посредством чего мы можем полностью выгрузить
физическую память, используя OpenBot firmware. К сожалению, подобного решения
не существует для Intel или AMD компьютеров.
Несмотря на все вышеперечисленные проблемы, программные методы
могут помочь в расследовании инцидентов, и я попытаюсь описать их в данной
статье. Основная цель этой статьи - демонстрация методов, которые используются
во время проведения процедуры сбора улик. Вся собранная информация может быть
использована для проведения, в последующем, анализа.
2. Анализ инцидента
Эта статья состоит из четырех частей:
- 2.1 Анализ окружения
- 2.2 Подготовка инструментов для анализа инцидента
- 2.3 Сбор информации о системе (пошаговые процедуры)
- 2.4 Анализ собранных данных
В этой статье будут рассмотрены пункты 2.1, 2.2 и частично
пункт 2.3
2.1 Анализ окружения
До сбора информации с работающей системы следует провести
анализ окружения. Сначала мы должны запустить сетевой сниффер и проанализировать
сетевой трафик, проходящий через скомпрометированную систему. Это обязательное
условие. Некоторые типы злонамеренной активности можно обнаружить только путём
записи и анализа сетевого трафика в реальном времени. Утилита tcpdump – превосходная
утилита для этой цели. Мой совет – сохранять пакеты в raw формате, в противном
случае могут возникнуть некоторые проблемы, связанные со спецификой настройки.
До того, как начинать какие-либо действия на скомпрометированной
системе, мы должны задокументировать процесс сбора информации на бумаге. Эта
процедура поможет нам избежать случайных ошибок во время проведения анализа.
Необходимо сделать дополнительные записи после каждого выполненного шага на
тот случай, если дело будет передано в суд.
Далее необходимо записать результаты выполнения команд во
время фазы сбора информации. Затем мы подключаемся к определённому хосту в
нашей локальной сети, на который мы будет отсылать информацию со скомпрометированной
системы. Запись информации на скомпрометированной системе может удалить следы
взлома. Для того, чтобы было как можно меньше конфликтов на скомпрометированной
системе мы должны отправить всю возможноую информацию на удалённый хост. Это
одно из важнейших правил в процессе анализа инцидента. И как было сказано
ранее – это требование не так уж легко выполнить.
Если у нас нет набора утилит, доступных для установки на
съемные носитель, настало время подготовить его для нашей скомпрометированной
системы. Используя инструменты из этого набора, мы будем собирать всю важную
информацию, начиная с наиболее нестабильных данных, заканчивая наименее нестабильными.
Далее будет описан способ подготовки внешнего носителя, содержащего необходимый
набор инструментов.
2.2 Подготовка инструментов для анализа инцидента
Важно помнить, что во время сбора информации мы должны следовать
следующим принципам:
- не запускать приложения на скомпрометированной системе. Почему? Взломщик
мог модифицировать системные команды (такие как netstat) или системные библиотеки
(такие как libproc), в результате полученная информация может быть недостоверной.
- не запускать программы, которые могут модифицировать метаданные файлов
или директории.
- все результаты исследования должны быть сохранены на удаленной машине.
Для переноса данных будем использовать утилиту netcat.
- Следует использовать утилиты для подсчёта значений хэша цифровых данных.
Это своего рода гарантия, что цифровые данные не были изменены. Нам необходимо
удостовериться, что данные не изменены и надежно сохранены на удалённом
хосте, поэтому мы так же сравним подсчитанные хэш значения ресурса и места
назначения. Иногда невозможно подсчитать значение хэша на скомпрометированном
хосте. Например, когда мы пытаемся использовать md5sum на /dev/mem устройстве
два раза подряд, каждый раз значение будет разным. Это происходит потому,
что каждый раз, когда мы загружаем программу в память (и соответственно
создаём новый процесс, который использует для работы память) мы меняем значение
памяти. Для расчета хэшей мы будем использовать утилиту md5sum.
- В некоторых случаях мы не сможем предотвратить запись информации в память
или swap скомпрометированной системы. Более подробно мы рассмотрим этот
случай в части 2.3. В результате мы будем использовать следующий набор инструментов
для расследования инцидентов, представленный в Таблице 1.
Таблица 1: Набор инструментов для расследования инцидента.
Как только мы успешно соберем все перечисленные утилиты,
мы скопируем их на съемный носитель (например, на CD-RW диск).
2.3 Сбор информации о системе (пошаговые процедуры)
Следующее и очень важное требование, то, что мы должны начать
собирать информацию в определённой последовательности, начиная с данных обладающих
наименьшим сроком жизни, заканчивая наиболее «живучей» информацией. Мы должны
помнить об этом в процессе сбора информации.
Шаг 1: Съёмка фотоаппаратом экрана скомпрометированной
системы.
Это своего рода скриншот, конечно же, мы должны использовать
цифровую камеру для этой задачи. Это простой шаг.
До того, как перейти к установке носителя, давайте сначала
задумаемся о последствиях, которые могут возникнуть при подключении носителя
к скомпрометированной системе. В данный момент давайте проигнорируем влияние
на память скомпрометированной системы.
Для монтирования съемного носителя мы должны использовать
ненадежную команду mount. Если всё пойдёт, согласно нашему плану, мы запустим
остальные команды с монтированного носителя.
Также мы должны исследовать изменения, которые произошли
в системе при использовании команды mount. Файлы и каталоги, которые были
изменены при использовании команды mount, приведены в таблице 2.
# strace /bin/mount /mnt/cdrom
Таблица 2: Файлы, которые были изменены после использования
команды mount.
Файл
|
Модифициронная мета-дата
|
/etc/ld.so.cache
|
Atime
|
/lib/tls/libc.so.6
|
Atime
|
/usr/lib/locale/locale-archive
|
Atime
|
/etc/fstab
|
Atime
|
/etc/mtab*
|
atime, mtime, ctime
|
/dev/cdrom
|
Atime
|
/bin/mount
|
Atime
|
* используя ключ “-n”, можно предотвратить доступ к этому
файлу.
Мы не учли ситуацию, в которой взломщик модифицировал команду
mount. Например, при запуске этой команды будет вызван специальный процесс,
который удаляет все доказательства злонамеренной активности на скомпрометированной
системе, вместо того, чтобы смонтировать носитель. Такой процесс называется
“deadman switch”. Но давайте предположим, что у нас все в порядке с командой
mount, и вернёмся к процессу сбора данных.
Я предполагаю, что была проверена каждая команда, которую
вы собирались сохранить на носителе, который в будущем будет использован на
скомпрометированной системе для сбора улик.
Давайте рассмотрим возможные проблемы, которые могут возникнуть
при монтировании внешнего носителя.
- После того, как мы вставили носитель в привод, процесс “Volume Manager”
смонтирует процесс автоматически. Какие файлы будут модифицированы? Все
ли из этих файлов перечислены в таблице 2?
- Предположим, в данный момент на скомпрометированной системе смонтирован
неизвестный носитель. Тогда необходимо предварительно размонтировать этот
носитель. Как сделать размонтирование наиболее безопасным? Могу предложить
два решения. Мы можем воспользоваться небезопасным вариантом – командой
umount или мы можем поместить «безопасную» команду umount на флоппи диск.
Следующий шаг – мы используем небезопасную команду mount для монтирования
флоппи и затем запускаем «безопасную» umount команду. Это немного запутано,
но эффективно. В этом случае мы используем только одну небезопасную команду
“mount”.
- Администратор не вошел в систему или даже хуже, пароль администратора
изменён взломщиком. К каким файлам будет осуществлён доступ, или какие файлы
будут изменены во время процесса входа в систему? Какие будут созданы дополнительные
процессы? Если пароль администратора был изменён, какие еще учетные записи
присутствуют в системе? Какие подверженные изменениям данные могут быть
собраны без доступа к оболочке? Открытые TCP/UDP порты, текущие соединения,
что ещё?
- Какие еще непредвиденные проблемы могут возникнуть?
Шаг 2: Монтирование носителя
Давайте начнём и смонтируем наш носитель, в данном случае
CD-ROM с нашими утилитами.
# mount -n /mnt/cdrom
Если процесс монтирования прошёл успешно, мы можем перейти
к наиболее важной стадии сбора данных. Запомните все результаты, сгенерированные
доверенными командами, которые должны быть отправлены на удалённый хост. Для
этого я использую утилиту netcat и pipe метод. Для того чтобы понять, какие
задачи выполняются на удаленном хосте, все запущенные команды на уязвимой
системе должны идти с приставкой “compromised”, а команды, запущенные на удаленном
хосте, с приставкой “remote”. Рассмотрим следующий пример.
Для отправки информации о реальной дате скомпрометированной
системы на удаленную систему (IP-адрес удалённого хоста, в данном случае,
192.168.1.100), на удаленном хосте нам необходимо открыть TCP порт следующим
образом:
(remote host)# nc -l -p 8888 > date_compromised
Далее, выполняем следующее на скомпрометированном хосте:
(compromised host)# /mnt/cdrom/date | /mnt/cdrom/nc 192.168.1.100
8888 -w 3
Для сохранности целостности данных, подсчитаем хэш значение
собранного файла и конечно задокументируем наши действия на бумаге.
(remote host)# md5sum date_compromised > date_compromised.md5
Иногда мы можем сгенерировать контрольные суммы на скомпрометированной
системе и отослать результаты на удалённый хост.
(compromised host)# /mnt/cdrom/md5sum /etc/fstab | /mnt/cdrom/nc
192.168.1.100 8888 -w 3
Step 3: Текущая дата
Результат представлен в UTC формате (Coordinated Universal
Time)
(remote)# nc -l -p port > date_compromised
(compromised)# /mnt/cdrom/date -u | /mnt/cdrom/nc (remote) port
(remote)# md5sum date_compromised > date_compromised.md5
Step 4: Кэшируемые таблицы
Первым делом мы должны собрать информацию из кэшируемых таблиц,
так как время жизни этой информации очень короткое. Я соберу информацию из
таблиц arp и routing.
Mac address cache table:
(remote)# nc -l -p port > arp_compromised
(compromised)# /mnt/cdrom/arp -an | /mnt/cdrom/nc (remote) port
(remote)# md5sum arp_compromised > arp_compromised.md5
Kernel route cache table:
(remote)# nc -l -p port > route_compromised
(compromised) # /mnt/cdrom/route -Cn | /mnt/cdrom/nc (remote) port
(remote)#md5sum route_compromised > route_compromised.md5
Шаг 5: Текущие и находящиеся в очереди соединения и открытые
TCP/UDP порты.
Далее собираем информацию о текущих соединениях и открытых
TCP/UDP портах. Информация о всех активных сырых сокетов будет собрана в восьмом
шаге.
(remote)#nc -l -p port > connections_compromised
(compromised)# /mnt/cdrom/netstat -an | /mnt/cdrom/nc (remote) port
(remote)#md5sum connections_compromised > connections_compromised.md5
В нашем случае мы можем использовать команду cat, вместо
команды netstat. Информация об открытых портах храниться в /proc псевдо файловой
системе (/proc/net/tcp и /proc/net/udp файлы).
Информация о текущих соединениях расположена в /proc/net/netstat
файле. Все данные в этих файлах представлены в hex формате.
Для примера: 0100007F:0401 in decimal is 127.0.0.1:1025.
Как упоминалось до этого, текущие соединения могут быть обнаружены
и проанализированы в записанном трафике. Исследование записанного трафика
может помочь в обнаружении руткита, загруженного в память ядра, который прячет
открытый порт. Мы должны удалённо просканировать скомпрометированный хост
и сравнить обнаруженные открытые порты с нашим результатом, полученным посредством
команды netstat. Но данный способ чреват большими повреждениями и мы снова
меняем содержимое скомпрометированной системы, в шаге семь я представлю альтернативный
метод обнаружения LKM руткитов.
В завершении первой
части
Далее мы предпримем несколько дополнительных шагов на скомпрометированной машине
до того, как выключим ее. Во второй части данной статьи мы исследуем способы
обнаружения злонамеренного кода путём сбора большего количества данных, отправленных
на удалённый хост. Также мы обсудим некоторые виды поиска, который может быть
произведен в безопасном окружении.
Часть вторая
1. Введение
В прошлом месяце, в первой части этой серии статей мы обсуждали подготовку и основные этапы, которые должны быть приняты во внимания при анализе работающей Linux системы, которая была скомпрометирована. Теперь мы продолжим наш анализ в поисках злонамеренного кода на работающей системе, а затем обсудим некоторые способы анализа данных, которые могут быть применены сразу же, как только данные будут переданы на наш удаленный хост.
Примечание:
Некоторые читатели после прочтения первой части этой статьи указали, что перед передачей любых цифровых данных со скомпрометированной машины, должен быть запущен доверенный шел. Я думаю, это хорошая идея, поэтому вначале мы должны статически скомпилировать, например bash, а затем скопировать его на наш сменный носитель информации. Шаг 2 должен выглядеть следующим образом:
(compromised)# /mnt/cdrom/bash
Мы должны помнить, что почти любой командный интерпретатор пишет каждую напечатанную команду в файл истории. Так как мы не хотим изменить локальную файловую системы, лучшим решением будет выключить запись истории команд.
Пожалуйста ознакомьтесь с первой частью этой статьи, а именно с разделами 2, 2.1, 2.2 и 2.3 (шаги с первого по пятый) прежде чем продолжить чтение.
2.3 Сбор информации о системе - продолжение
Шаг 6 - Образ физической памяти
Один из шагов в процессе сбора цифровой информации о системе это полное копирование памяти системы. Мы можем получить доступ к физической памяти напрямую копируя устройство /dev/mem или kcore файл. Файл kcore это RAM (Random Access Memory, Память со случайным доступом -- примечание переводчика) в операционной системе Linux. Он может быть найден в псевдо файловой системе, которая примонтирована в директории /proc. Размер этого файла равен размеру физической памяти, расширенной примерно до 4 Кб. Преимущество использования kcore состоит в том, что он представлен в формате ELF и поэтому может быть отлажен при помощи отладчика gdb. gdb - отличный инструмент, который позволяет трассировать и анализировать небольшие части кода или памяти. В разделе 2.7 я покажу, как использовать эту утилиту для более глубокого автономного анализа. Но в основном мы будем использовать более универсальные средства, типа grep, string и шестнадцатеричных редакторов.
В дополнение, чтобы должным образом изучать физическую память, мы должны иметь кое-какую информацию из таблицы страниц - структуры данных, использующиеся при преобразовании виртуального адреса в физический. Мы должны знать, что физическая память делится на страницы виртуальной памяти. В таблицах страниц мы можем найти информацию о порядке страниц (4 Кб на страницу в процессорах от Intel) записанных в физическую память.
Как было упомянуто ранее, мы должны помнить, что, используя ПО, работающее с динамической памятью, мы изменяем содержимое памяти. Хуже того, мы можем перезаписать возможное доказательство.
В примере ниже я скопировал образ памяти, используя псевдо файловую систему /proc:
(remote)#nc -l -p port > kcore_compromised
(compromised)#/mnt/cdrom/dd < /proc/kcore | /mnt/cdrom/nc (remote) port
(remote)#md5sum kcore_compromised > kcore_compromised.md5
При полном копирование памяти системы мы копируем и размещенные (allocated) и не размещенные (unallocated) данные, потому что процесс копирует полный образ физической памяти. В девятом шаге мы попробуем скопировать определенный процесс, используя /proc. Должен упомянуть, что, используя /proc, мы можем изменить раздел подкачки (swap). Копируя подозрительный процесс, какие-то страницы мы заставим считаться из файла подкачки в память, а какие-то записаться в него. Другая важная особенность состоит в том, что мы копируем только ту память, в которой размещены данные процесса.
Шаг 7 - Список модулей загруженных в адресное пространство ядра операционной системы
Мы должны убедиться, что собранные данные полны и что результаты работы команд netcat и lsof не изменяются на уровне ядра. Итак, мы должны выяснить, какие модули в настоящее время загружены в память.
(remote)# nc -l -p port > lkms_compromised
(compromised)#/mnt/cdrom/cat /proc/modules | /mnt/cdrom/nc (remote) port
(remote)# nc -l -p port > lkms_compromised.md5
(compromised)# /mnt/cdrom/md5sum /proc/modules | /mnt/cdrom/nc (remote) port
К сожалению, некоторые из злонамеренных модулей не могут быть выявлены таким способом. Чтобы проверить информацию о загруженных модулях, полученную из /proc/modules, я воспользуюсь методом, описанном в недавнем номере Phrack Magazine, в статье "Finding hidden kernel modules (the extrem way)". Модуль hunter.o проверяет цепочки модулей, загруженных в адресное пространство ядра.
(compromised)#/mnt/cdrom/insmod -f /mnt/cdrom/hunter.o
В том случае если необходимо произвести принужденную загрузку hunter.o из-за несоответствия версий, я буду использовать флаг "-f switch". Это случается когда версия ядра скомпрометированной системы отличается от версии ядра системы, на которой был скомпилирован модуль hunter.o . Лучшее решение состоит в том, чтобы пересобрать модуль для каждой версии ядра, в адресное пространство которого hunter.o будет загружен. Если мы знаем версию ядра, которое используется на скомпрометированной машине, мы может загрузить соответствующий исходный код с www.kernel.org и включить определенные заголовочные файлы того ядра в наш модуль.
(remote)#nc -l -p port > modules_hunter_compromised
(compromised)#/mnt/cdrom/cat /proc/showmodules && /mnt/cdrom/dmesg | /mnt/cdrom/nc (remote) port
(remote)#md5sum modules_hunter_compromised > modules_hunter_compromised.md5
Теперь мы можем сравнить результаты. Также мы должны обратить внимание на размер модулей. Иногда злонамеренный код может быть внедрен в тело "легального" модуля.
Последнее, что мы должны сделать - копии таблиц экспорта модулей ядра. Иногда некоторые простые lkm rootkit'ы экспортируют свои функции. Изучая файл ksyms, мы можем обнаружить присутствие злоумышленника в системе.
(remote)#nc -l -p port > ksyms_compromised
(compromised)#/mnt/cdrom/cat /proc/ksyms | /mnt/cdrom/nc (remote) port
(remote)# nc -l -p port > ksyms_compromised.md5
(compromised)#/mnt/cdrom/md5sum /proc/ksyms | /mnt/cdrom/nc (remote) port
Мы также можем использовать другие средства для обнаружения злонамеренных модулей (такие как kstat или kern_check), но, к сожалению, все они используют файл System.map . Этот файл генерируется после сборки ядра. Если администратор не скопирует этот файл и не сохранит его контрольную сумму, мы не должны доверять полученным адресам системных вызовов. Даже если адреса системных вызовов правильны, злоумышленник может использовать другие изощренные методы сокрытия злонамеренного модуля в памяти ядра. Например, adore-ng rootkit заменяет подпрограмму обработчик, осуществляющий вывод листинга директории.
Если мы уверены, что адреса системных вызовов в файле System.map или ksyms не изменялись, мы можем проверить, не изменены ли злоумышленником некоторые из адресов системных вызовов в таблице системных вызовов. Более подробно мы рассмотрим это в разделе 2.7 .
Шаг 8 - Список активных процессов
Информация обо всех процессах, открытых портах и файлах может быть собрана с помощью утилиты lsof. Естественно, этой информации можно доверять только в том случае, если мы не обнаружим в памяти lkm rootkit'ов. В дополнение, на следующем шаге мы сделаем копии подозрительных процессов. Копируя определенный процесс, мы можем отделить злонамеренные данные от памяти скомпрометированной системы, которую мы скопировали полностью. Как вы помните, мы сделали образ всей памяти системы (kcore или /dev/mem) в шаге 6.
(remote)#nc -l -p port > lsof_compromised
(compromised)#/mnt/cdrom/lsof -n -P -l | /mnt/cdrom/nc (remote) port
(remote)#md5sum lsof_compromised > lsof_compromised.md5
Теперь мы должны проанализировать результаты работы lsof. Если хотя бы один из активных процессов кажется подозрительным, сейчас самое время сделать его копию. Также, если из результатов работы lsof мы видим, что программа, которая создала процесс, была удалена злоумышленником, у нас еще есть шансы восстановить ее. Я покажу, как сделать это в следующем разделе.
Ниже, я перечислил несколько примеров подозрительных процессов:
- Процесс слушает нетипичный TCP/UDP порт или открытый raw сокет
- Процесс имеет соединение с удаленным хостом
- Программа, удаленная после выполнения
- Файл, открытый процессом был удален (например: лог файл)
- Необычное имя процесса
- Процесс был создан несуществующим или непривилегированным пользователем
Шаг 9 - Создание копий подозрительных процессов
Я буду использовать утилиту pcat для создания образа памяти выделенной процессу.
(remote)#nc -l -p port > proc_id_compromised
(compromised)#/mnt/cdrom/pcat proc_id | /mnt/cdrom/nc (remote) port
(remote)#md5 proc_ip_compromised > proc_ip_compromised.md5
Кроме этого, мы можем сделать копии лишь некоторых выборочных данных процесса. Более подробная информация об этом в следующем разделе.
Шаг 10 - Полезная информация о скомпрометированной системе
Наш следующий шаг - сбор некоторой полезной информации о скомпрометированной системе. Эта информация нужна для того, чтобы выполнить надлежащее описание инцидента и создать копии всех системных файлов. Помните, что все результаты вы должны отослать на доверенный хост. В таблице 3 находятся команды, которые должны быть выполнены на скомпрометированной системе.
Таблица 3: Полезная информация о скомпрометированном хосте
Команда |
Описание |
/mnt/cdrom/cat /proc/version |
Версия операционной системы |
/mnt/cdrom/cat /proc/sys/kernel/name |
Имя хоста |
/mnt/cdrom/cat /proc/sys/kernel/domainame |
Доменное имя |
/mnt/cdrom/cat /proc/cpuinfo |
Информация о аппаратном обеспечении |
/mnt/cdrom/cat /proc/swaps |
Все разделы файлов подкачки |
/mnt/cdrom/cat /proc/partitions |
Все локальные файловые системы |
/mnt/cdrom/cat /proc/self/mounts |
Примонтированные файловые системы |
/mnt/cdrom/cat /proc/uptime |
Продолжительность работы с момента загрузки системы (Uptime) |
Шаг 11 - Текущее время
Последний шаг - сбор информации о текущем времени.
(remote)#nc -l -p port > end_time
(compromised)# /mnt/cdrom/date | /mnt/cdrom/nc (remote) port
Теперь мы достигли того момента, когда можем выключить скомпрометированную систему. Запомните, что для завершения работы системы нельзя использовать какие-либо стандартные команды. Мы должны выдернуть силовой кабель из системного блока или источника бесперебойного питания.
2.4 Образы файловой системы
Перед выключением питания скомпрометированной системы мы сделали копии всех файловых систем и разделов подкачки, но еще не отключились от нее. Я предлагаю сделать это после выключения питания системы. Таким образом, мы сможем убедиться, что содержание скомпрометированной файловой системы больше не изменялось. Напомню, что раздел подкачки, изменялся на протяжении всего процесса сбора информации и некоторые из доказательств, возможно, были перезаписаны.
Следующий шаг состоит в загрузки операционной системы со съемного носителя. Мы можем использовать любой дистрибутив Linux, по умолчанию не монтирующий все локальные файловые системы. Теперь мы можем сделать копии всех локальных разделов или всего жесткого диска.
2.5 Основы анализа данных
Сейчас давайте снова рассмотрим процессы, которые мы упоминали в Шаге 8, где мы использовали утилиту lsof. Давайте более подробно посмотрим на эту ситуацию и рассмотрим два примера:
Пример 1: Программа, которая создала процесс, была удалена.
В этом случае, файл, который инициировал процесс, все еще находится в памяти. Чтобы его восстановить, мы должны знать идентификатор процесса, созданного программой. В каталоге /proc мы найдем исполняемый файл. Этот файл - копия удаленного. Мы должны отправить этот файл на доверенный хост.
После восстановления удаленного файла мы можем извлечь из него информацию. Мы можем выбрать один из двух вариантов: статический анализ (дизассемблированием) или динамический анализ (исполнением этого файла в контролируемой среде).
Пример 2: Файл, открытый активным процессом был удален (например: лог файл).
Фрагмент результата работы lsof представлен ниже:
smbd 3137 root rtd DIR 8,1 4096 2 /
smbd 3137 root txt REG 8,1 672527 92030 /usr/bin/smbd -D
smbd 3137 root mem REG 8,1 485171 44656 /lib/ld-2.2.4.so
...
smbd 3137 root 16u IPv4 976 TCP *:https (LISTEN)
smbd 3137 root 17u IPv4 977 TCP *:http (LISTEN)
...
smbd 3137 root 20w REG 8,1 253 46934 /var/log/httpd/access_log (deleted)
...
Чтобы восстановить этот файл мы должны просмотреть содержимое каталога fd (дескрипторы файлов), находящегося в директории /proc/3137.
(remote)# nc -l -p port > ls_from_proc_3137
(compromised)# /mnt/cdrom/ls -la /proc/3137/fd/ | /mnt/cdrom/nc (remote) port
(remote)# more ls_from_proc_3137
l-wx------ 1 root root 64 Aug 10 21:03 12 -> /var/log/httpd/access_log (deleted)
...
Как мы видим, первый файловый дескриптор удален. Все что мы должны сделать, это скопировать этот файл на доверенный хост, используя утилиту netcat.
(remote)# nc -l -p port > deleted_access_log
(compromised)# /mnt/cdrom/cat /proc/3137/fd/1 | /mnt/cdrom/nc (remote) port
Это не единственный способ восстановления этого файла. Мы также можем восстановить его путем анализа освобожденных i-nodes и блоков данных.
2.6 Поиск ключевых слов
В этой части статьи я хотел бы рассказать об одном из методов исследования процессов и образов памяти. Учтите, что все описанное происходит на удаленном доверенном хосте, после получения образов памяти. Самое большое преимущество этого метода состоит в том, что мы не должны знать используемый низкоуровневый язык (видимо речь идет об ассемблере -- примечание переводчика). Я буду использовать следующие простейшие утилиты для поиска признаков вторжения:
- strings
- less
- grep
Мы соберем все печатные символы из файла образа, используя утилиту strings. Настройки по умолчанию дают нам возможность просмотреть все последовательности печатных символов, состоящие не менее чем из 4 символов. Чтобы добавить смещение относительно начала файла нужно использовать флаг "-t".
$ strings -t d kcore > kcore_strings
$ md5sum kcore_strings > kcore_strings.md5
Утилита grep и регулярные выражения важны на этом начальном этапе анализа. Через несколько минут мы сможем найти доказательства вторжения. Мы должны подумать, какие данные мы собираемся искать, - например, мы ищем команды, напечатанные злоумышленником, IP адреса, пароли или даже расшифрованную часть злонамеренного кода?
Ниже перечислены несколько примеров ключевых слов для поиска. Мы будем использовать их, чтобы найти доказательства вторжения в нашем файле kcore_strings.
- Имя или префикс хоста скомпрометированной системы
$ grep "root@manhunt" kcore_strings
$ grep "]#" kcore_strings
11921096 [root@manhunt]#
16643784 [root@manhunt root]#
30692969 ]#]#
Мы видим смещения некоторых строк относительно начала файла. Следующим шагом будет открытие файла в текстовом редакторе и переход на позицию файла, находящеюся рядом с полученными смещениями. Если нам повезет, мы найдем и другие команды, выполненные в прошлом. Но мы должны помнить, что страницы виртуальной памяти в физическую память и раздел подкачки пишутся неорганизованно, поэтому наши выводы могут быть полностью не верными.
$ less kcore_strings
/11921096
11921096 [root@manhunt]#
11921192 /usr/bin/perl
11921288 perl apache_mod_exploit.pl
...
Пример выше показывает несколько команд исполненных на скомпрометированной системе.
- Имена файлов и директорий
$ grep -e "/proc/" -e "/bin/" -e "/bin/.*?sh" kcore_strings
$ grep -e "ftp" -e "root" kcore_strings
$ grep -e "rm -" kcore_strings
$ grep -e ".tgz" kcore_strings
>
- IP адреса и доменные имена
$ grep -e "[0-9]+.[0-9]+.[0-9]+.[0-9]+" kcore_strings
$ grep -e ".pl" kcore_strings
2.7 Подробный анализ вторжения
В этом заключительном разделе я покажу, как нужно изучать файл kcore с помощью gdb. Вначале я опишу, как проверить правильность адресов системных вызовов в таблице системных вызовов. Изменения адреса системного вызова - самый простой путь установить lkm rootkit на скомпрометированную систему. В следующем примере я покажу, как просмотреть все процессы, который были запущенные на недавно скомпрометированной системе. Результаты этих примеров могут быть сравнены с результатами, полученными в шагах 7 и 8.
Для начала анализа нам нужна следующая информация:
- образ памяти в формате ELF (см. Шаг 6)
- скомпилированный образ ядра (мы должны примонтировать образы файловых систем и скопировать файл vmlinux-* из директории /boot)
- таблицы экспорта (System.map находится в директории /boot скомпрометированной системы, также мы можем использовать файл ksyms - см. Шаг 7)
- исходные коды ядра, используемого на скомпрометированной машине (если исходные коды не доступны, мы должны загрузить необходимую версию со следующего веб сайта: www.kernel.org)
Затем запустим gdb:
#gdb vmlinux kcore
GNU gdb Red Hat Linux (5.1.90CVS-5)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
warning: core file may not match specified executable file.
Core was generated by `ro root=/dev/sda2 hdc=ide-scsi'.
#0 0x00000000 in ?? ()
(gdb)
Теперь мы готовы начать наши исследования.
Пример 1: Проверка адресов системных вызовов.
Вначале нам нужно найти адрес таблицы системных вызовов (sys_call_table). Почти в каждой Linux системе эта информация экспортируется и может быть найдена в файле Symbol.map .
# cat Symbol.map | grep sys_call_table
c02c209c D sys_call_table
Теперь мы можем просматривать элементы таблицы системных вызовов. Каждый элемент это адрес системного вызова. Чтобы правильно их интерпретировать, рекомендую посмотреть файл entry.S из исходников ядра скомпрометированной системы. В этом файле находится упорядоченный список системных вызовов.
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
...
Та же последовательность и в sys_call_table. Чтобы просмотреть первые 10 адресов системных вызовов сделаем следующее:
(gdb) x/10x 0xc02c209c
0xc02c209c <sys_call_table>: 0xc01217c0 0xc011ac50 0xc0107510 0xc0138d50
0xc02c20ac <sys_call_table+16>: 0xc0138e50 0xc0138880 0xc01389b0 0xc011b010
0xc02c20bc <sys_call_table+32>: 0xc0138930 0xc01445c0
Адрес 0xc01217c0 - это системный вызов sys_ni_call, адрес 0xc011ac50 - системный вызов sys_exit, и т.д.
Если мы доверяем адресам из System.map или ksyms, мы можем сравнить почти каждый адрес. Конечно, злоумышленники изменяют адрес не каждого системного вызова, есть несколько популярных, таких как sys_read, sys_getdents или sys_write.
Пример 2: Список активных процессов.
Чтобы получить полный список запущенных процессов, мы должны узнать адрес структуры init_task_union. Эта структура указывает на первый дескриптор процесса, который называется "process 0" или "swapper".
# cat Symbol.map | grep init_task_union
c02da000 D init_task_union
Теперь нам нужно узнать, как выглядит эта структура. Пример структуры init_task есть в заголовочном файле sched.h в исходниках ядра скомпрометированной системы. Структура task_struct также находится в этом файле.
#define INIT_TASK(tsk)
{
state: 0,
flags: 0,
sigpending: 0,
addr_limit: KERNEL_DS,
exec_domain: &default_exec_domain,
...
run_list: LIST_HEAD_INIT(tsk.run_list),
time_slice: HZ,
next_task: &tsk,
prev_task: &tsk,
p_opptr: &tsk,
...
Самые важные для нас поля - prev_task и next_task каждого дескриптора процесса (тип task_struct). Они помогут нам создать список активных процессов. Поле next_task это указатель на дескриптор следующего процесса, prev_task - указатель на дескриптор предыдущего процесса.
В примере ниже, я выведу список фрагментов дескриптора процесса (тип task_struct), который находится по адресу 0xc514c000.
(gdb) x/180x 0xc514c000
...
0xc514c040: 0x00000000 0xffffffff 0x00000004 0xc1be0000
0xc514c050: 0xc4dac000 0xc5ea8e40 0xc5ea8e40 0xc02c56d4
...
0xc514c070: 0x00000001 0x00001ace 0x00001ace 0x00000000
...
0xc514c230: 0xffffffff 0xffffffff 0x61620000 0x00006873
0xc514c240: 0x00007974 0x00000000 0x00000000 0x00000000
...
где:
0xc1be0000 - next_task
0xc4dac000 - prev_task
0x00001ace - идентификатор процесса = 6268 в десятеричной системе счисления
0x61620000 и 0x00006873 - имя процесса (comm таблица в структуре) = "bash" в данном случае
Чтобы вывести дескриптор следующего процесса (тип task_struct), сделаем так:
(gdb) x/180x 0xc1be0000
Получая информацию о каждом дескрипторе процесса, мы можем узнать полный список процессов.
3. Резюме
Цель этой статьи показать некоторые методы, используемые для сбора информации, простого и углубленного анализа данных. В частности, я сосредоточился на тех данных, которые были бы полностью потеряны после выключения системы. Эта часть анализа инцидента и процесса реагирования на инцидент очень сложна в реализации; в течение всех этапов мы должны быть очень осторожными и документировать каждое действие. Мы также посмотрели на некоторые преимущества и недостатки программного обеспечения для сбора информации с работающей системы. И наконец, несмотря на то, что я показал, как выглядит процедура сбора информации в Linux, мы также можем выполнить почти такие же шаги на других операционных системах. В следующей статье вы узнаете, как получить необходимую информацию с работающей Windows системы.
Ссылки
>
- Adore-ng rootkit, http://stealth.7350.org/rootkits.
- Alessandro Rubini, Jonathan Corbet. Linux Device Drivers, 2nd Edition. O'Reilly; 2001.
- Dan Farmer, Wietse Venema. Column series for the Doctor Dobb's Journal. http://www.porcupine.org/forensics/column.html.
- Daniel P. Bovet, Marco Cesati. Understanding the Linux Kernel, 2nd Edition. O'Reilly; 2002.
- Kernel source code. http://www.kernel.org.
- Linux manual pages.
-
National Institute of Standards and Technology. Computer Security Incident Handling Guide. http://csrc.nist.gov.
- PHRACK #61. Finding hidden kernel modules (the extrem way) by madsys. http://www.phrack.org.
- RFC 3227. Guidelines for Evidence Collection and Archiving.
- Smith Fred, Bace Rebecca. A guide to forensic testimony. Addison Wesley; 2003.
- Symantec Corporation. CodeRed Worm. http://securityresponse.symantec.com.
- The Honeynet Project. Scan 29. http://www.honeynet.org.
- The SANS Institute. Incident Handling step by step. http://www.sans.org.