Исследуем Galileo, шпионский софт Hacking Team для слежки за пользователями по всему миру
Прошел почти год с момента взлома компании Hacking Team, специализирующейся на разработке шпионского программного обеспечения для спецслужб со всего мира. Самое интересное, что в Сеть утекли исходники их платформы Galileo, но до сих пор не появилось более-менее подробного их обзора или анализа. Сегодня мы постараемся исправить это упущение.
Galileo в телескопе
Не будем терять время на ненужные предисловия и сразу же перейдем к делу. Вся платформа разбита на Git-репозитории c различным назначением и под разные платформы: Windows, Linux, BlackBerry, Windows Mobile, iOS, macOS, Android, но мы будем рассматривать только код под Windows и Linux. Сами модули, содержащиеся в репозиториях, можно поделить на несколько типов:
- модули, названия которых начинаются с приставки vector- , предназначены для проникновения и закрепления в системе;
- модули с приставкой core- — собственно основное ядро платформы, отвечает за сбор и передачу «хозяину» необходимой информации.
Есть также несколько вспомогательных модулей, выносить их в отдельную категорию не будем, но обязательно рассмотрим. В основном вся платформа, заточенная под Windows, разработана на C++ (небольшая часть на Python), Linux-код написан на C. Также есть код и на Ruby — на нем разработана серверная часть продукта, мы же будем рассматривать только клиентскую. Но давай уже перейдем к практической части («Меньше слов, покажите мне код» — Линус Торвальдс) и посмотрим, какие приемы использовали парни из Италии, чтобы не засветиться на радарах и доставить свое программное обеспечение до «клиентов».
Рис. 1. Схема связей модулей Galileo
Xakep #215. Второй факторПытаемся проникнуть в систему и закрепиться
Начнем с модуля vector-dropper. В принципе, внутри него все реализации dropper’а под различные ОС, но нас будет интересовать только RCSDropper под Windows (платформа под Linux полностью реализована в отдельном Git-репозитории core-linux, все, что касается модулей под Linux, разберем чуть позже).
RCSWin32DropperЭтот модуль обеспечивает первоначальный этап заражения атакуемого объекта: «размазывает» по файловой системе необходимые файлы, их расшифровывает, обеспечивает persistence и прочее. В коде присутствует очень много комментариев, название самих переменных, модулей, все сделано очень удобно, много help-информации при работе с CLI — в общем, видно, что ребята старались, делали продукт, удобный для заказчика. Все приведенные вставки кода — это Copy-Paste из исходного кода, все комментарии разработчиков тоже по возможности сохранены, для удобства некоторые переведены. Для облегчения сборки контейнера, который потом будет доставляться на целевую машину, разработчики сделали полноценный CLI в двух модификациях. Первый вариант сборки:
В данном случае <input> — чистый файлик, который перемешивается с «необходимым» кодом, в нашем случае — <scout> (модуль «разведчик», определяет, находится ли семпл в песочнице, выявляет наличие антивирусных средств), код данного модуля рассмотрим ниже. Итак, за «микс» между чистым файлом и необходимой полезной нагрузкой отвечает код из MeltFile.cpp , а именно функция MeltFile , она и инициализирует процесс скрещивания двух файлов. Подробно на том, как она это делает, останавливаться не будем, но основная цель — подмена текущего, чистого EntryPoint на функцию DropperEntryPoint из DropperCode.cpp (собственно, уже в этом коде и выполняется вся магия по извлечению модулей, которая будет описана ниже).
Второй экземпляр поставки отличается только тем, что он «запихивает» в наш файл-контейнер все необходимые модули — ядро, драйвер, конфигурационный файл и остальные:
DropperEntryPointНа данном этапе стоит более подробно раскрыть работу DropperEntryPoint — функции, которая будет распаковывать основные модули на целевую машину. С самого начала кода можно увидеть обход эмулятора Avast, причем простым циклом на уменьшение — наверное, просто тянут время, и таким образом AV прекращает проверку эмулятором после 250–300 операций. В принципе, данная техника не нова:
После восстанавливаем стандартным способом Entry Point, через call pop , затем находим секцию внутри текущего модуля с пометкой <E> (чтобы обнаружить смещение в коде, откуда начинаются модули, которые будем распаковывать), причем ищем обычным перебором по байтам в виде ассемблерной вставки:
Ассемблерная вставка используется с целью обхода Dr.Web, по крайней мере так указано в комментариях: // *** Find the ending marker of data section <E> - ASM because of Dr.Web :) .
Следующим этапом будет восстановление собственной таблицы импорта:
Код у функций resolveLoadLibrary и resolveGetProcAddress почти идентичный. Суть такая, что через PEB производится поиск сначала экспорта kernel32 , а затем двух ее самых важных для малварщика функций: GetProcAddress и LoadLibrary . Единственное, на что хочется обратить внимание, — это строка:
Как видно из комментария, для обхода f-secure сравнение имени модуля в списке PEB и строки с символами Kernel32.dll начинается со второго символа. После этого определяем, находится ли код под пристальным контролем Microsoft Essential или нет:
Выделяем память на куче и кладем туда полный путь доступа к файлу, содержащему указанный модуль, которым владеет текущий процесс. В результате если это совпадает с Microsoft Security Client , то прекращаем выполнение и переходим на оригинальный код нашего контейнера. В противном случае поочередно извлекаем все, что есть в контейнере.
Распаковка
Так же как и вариантов упаковки, есть два варианта распаковки кода. В случае упаковки с модулем разведки все модули, находящиеся в контейнере, зашифрованы RC4 и упакованы APlib (библиотека для сжатия исполняемых файлов для Microsoft Windows). В случае с модулем scout файл извлекается в директорию %Autorun% , таким образом обеспечивая себе persistence.
Если модуля разведчика не было, то все файлы поочередно извлекаются в директорию Temp/Microsoft . Вот кусок кода, который отвечает за извлечение файлов:
Разведаем обстановку
Следующий модуль, который будем анализировать, — scout. В нем как раз происходит вся магия, позволяющая определить, запущены ли мы в песочнице, есть ли поблизости приложения, которые не подходят по «политическим» моментам, запущены ли мы в виртуализации и прочее. Начнем с функции AntiVM() в antivm.cpp :
В ней как раз и стартует проверка наличия сред виртуализации и песочниц. Начнем с детекта Cuckoo Sandbox, как наиболее интересного из всех методик обхода. Метод основан на том, что при выполнении данного участка кода библиотека cuckoomon.dll упадет:
Как обнаруживаются VMware и VirtualBox? Оба детекта почти идентичны: через язык запросов WMI запрашиваются значения некоторых параметров (ниже указаны инициализации строк, которые будут проводить WMI-запрос). Например, параметры BIOS, PnP, в случае с VirtualBox это:
При проверке на наличие VMware:
После от полученных путем WMI-запросов значений вычисляют хеши SHA-1 и сравнивают с предопределенными константами:
Самый тихий модуль
Следующим посмотрим модуль vector-silent. В общем представлении Vector-Silent — это модуль для скрытой установки полезной нагрузки в зараженную систему. В принципе, ничего особенного в нем нет, поэтому внимание на нем заострять не будем. Анализ вектора начнем с функции WinMain() . Суть работы модуля проста. Для начала восстанавливаем таблицу импорта стандартным для малварщиков способом — перебирая таблицы экспорта двух динамических библиотек: kernel32 и ntdll . Набор данных функций не представляет собой ничего необычного: VirtualAlloc(), VirtualFree(), GetModuleFileNameA(), GetEnviromentVariableA(), GetFileAttributesA(), CreatDirectoryA(), SetCurrentDirectoryA(), SetFileAttributesA(), CreateFileA(), GetLastError(), WriteFile(), CloseHandle(), FreeLibrary(), DeleteFileA(), swprintf(), GetCurrentProcessId(), GetModuleHandle() .
Далее следует небольшой кусок кода с вызовом различных функций (получаем значения ключа реестра, текущую локаль, версию ОС и так далее), но он не несет для авторов никакой полезной нагрузки и даже обрамлен комментариями типа // FAKE FAKE FAKE и // END FAKE FAKE FAKE — просто способ запутать анализ. Основная полезная нагрузка модуля начинается в функции DropperEntryPoint() . В самом начале работы проверяется контроль Microsoft Essential для x86- и x64-разрядных систем. Для последнего сравнение ведется по строке :\myapp.exe . При совпадении работа завершается. Далее определяются пути к временным директориям из переменных окружения ( TMP или TEMP ). По временной директории вычисляется ее родительская, и проверяется наличие в ней папки с именем Microsoft .
Следующий участок кода функции — последовательный вызов функции dump_to_file() , которая записывает на диск основные функциональные модули: core, core64, config, driver, driver64, codec. По сути, функция dump_to_file() всего лишь проверяет актуальность входных аргументов, после чего в случае успеха передает управление функции DumpFile() . В функции DumpFile() производится шифрование/расшифровка (алгоритм RC4, ключ 256 бит) переданного в качестве аргумента тела файла и запись в рабочую директорию. В завершение DropperEntryPoint() вызывается функция CoreThreadProc(DropperHeader*) . Здесь необходимо упомянуть, что в структуре DropperHeader :
присутствуют поля dllPath и eliteExports . Последнее содержит имена двух функций (условные названия HFF5 и HFF8 ), которые экспортируют библиотеку из dllPath . Сначала загружается модуль по расположению header->dllPath , затем получается адрес функции HFF5 и вызывается со следующими аргументами:
- (1) строка <%systemroot%\System32\rundll32.exe "<dllPath>",<HFF8>> ;
- (2) NULL ;
- (3 и 4) пустые экземпляры структур типов STARTUPINFO и PROCESS_INFORMATION .
Таким образом запускаются основные модули.
Главный боец
Итак, перейдем к модулю soldier-win. Если бегло взглянуть на названия файлов с исходниками (см. рис. 2), то можно и без их анализа сделать вывод, что код, который в них написан, выполняет основную активность на зараженной машине: скриншоты, камера, сбор информации о паролях в различных соцсервисах, почтовых клиентах, есть даже код, который устанавливает местоположение по Wi-Fi.
Начнем с модуля main.cpp , а в нем с функции int CALLBACK WinMain , которая является EP для графических Windows-приложений. Кода в этом модуле хватит на целый номер журнала, но есть несколько интересных моментов, на них и сконцентрируемся. Например, функция InitScout() . По названию понятно, что она отвечает за инициализацию модуля. В первую очередь определяется «чистота» почвы, на которой модуль запускается. Делается это уже известным нам способом — AntiVM() .
Вообще, самое интересное в функции InitScout() — это расшифровка конфига в LoadConf() . По-видимому, создатели предполагали несколько способов хранения конфигов: в самом коде и в ветке реестра. При этом использование ветки работает только в режиме debug. Конфиг зашифрован на AES в режиме CBC. В нем, помимо сервера, указаны основные цели, которые преследуют на текущей зараженной машине. Для каждой из них создается отдельный поток. Например, сбор информации о соцсервисах:
Сбор информации о соцсервисах работает следующим образом: первым делом забираются все имеющиеся на машине файлы cookie из браузеров Firefox, IE, Chrome. Первый и третий хранят cookie в базе данных SQLite, поэтому легко достать их обычным SQL-запросом:
При сборе кукисов из IE просто проходим по каталогу с интересующими нас файлами и применяем к ним простенький парсер, получая необходимую инфу:
Далее собирается информация о геокоординатах. В данном случае используется несколько подходов. Первый — это сбор информации о ближайших точках доступа Wi-Fi. Второе — сбор координат из постов новостной ленты Facebook, а также из размещенных в соцсети фото.
А как обстоят дела с Linux?
Ну а теперь окунемся немного в мир Linux. В данном случае весь код написан на С и структурно представляет собой почти полный аналог виндовой версии: ядро системы, дроппер и парочка вспомогательных модулей.
Рис. 2. Структура модулей для Linux
В первую очередь посмотрим код дроппера. В принципе, ничего специфического в нем нет, и в самом начале исходного кода dropper.c авторы оставили формат будущего агента:
Если сопоставить это со скриптом сборки build.php , то действительно подтверждается структура формата dropper’a:
Далее модуль установки всей полезной нагрузки, опрашивая различные переменные окружения ( SUDO_USER, USER, USER_NAME ), определяет, запущен ли он под рутом:
Соответственно, если это так, то структура passwd из pwd.h , предназначенная для хранения информации о пользователе:
заполняется информацией, полученной из /etc/passwd ; если агент запущен не в режиме суперпользователя, то мы получаем его имя командой pp = popen(SO"who -q", "r") , а вся остальная информация описанным выше способом — через /etc/passwd . Далее полученная информация используется для смены текущего каталога на домашний каталог пользователя.
Следующий этап — извлечение всей нагрузки на диск. Производится за счет маркеров, тегов и размеров, которые добавлялись в агент через скрипт сборки. При этом в качестве каталога установки выступает /var/crash/.reports-%u-%s", uid, tag или /var/crash/.reports-%u-%s", uid, tag . Конфигурационный файл пишется в .cache :
а ядро текущего агента по аналогичной схеме в .whoopsie-report . Для поддержания живучести агента в системе проводится несколько операций, обеспечивающих его автозапуск:
А именно: в текущем каталоге пользователя в подкаталоге .config/autostart создается файл .desktop с путем до ядра. Ну и наконец, производится запуск ядра командой execl .
Ядро для системы LinuxУделим немного внимания ядру Galileo для системы Linux. Кратко рассмотрим инициализацию ядра и более подробно поговорим о модулях полезной нагрузки.
Рис. 3. Перечисление имен модулей core Linux
Итак, на этапе инициализации первым делом определяется пользователь, от которого запущен процесс, имя процесса, имя машины, производится инициализация необходимых библиотек и парсится конфигурационный файл parseconfig(SO".cache") , при этом конфигурационный файл зашифрован:
Сами ключи шифрования жестко прописаны в структуре params :
В коде видно, что все настройки модуль носит с собой, при этом они оформлены в JSON-формате. Соответственно, их разбор оформлен в виде вызовов стандартных функций для работы с JSON. В настройках указаны время и периодичность запусков полезной нагрузки и, главное, набор этих самых полезных нагрузок и параметры их запуска. Чтобы обеспечить скрытность получения результатов работы, собранная информация с зараженной машины передается в pipe и шифруется на AES-128-CBC, при этом ключ шифрования жестко указан в коде в поле структуры bps evidencekey .
Полезные нагрузки
Ну а теперь пробежимся по наиболее интересным модулям полезной нагрузки.
-
module_addressbook собирает информацию из адресной книги Skype, которая хранится в SQLite3 базе данных:
После запросов к БД:
Далее полученные данные необходимо расшифровать. Тут на помощь приходит Network Security Services (NSS) — набор библиотек, предназначенных для разработки защищенных кросс-платформенных клиентских и серверных приложений. В его состав входит функция PK11SDR_Decrypt (SECItem *data, SECItem *result, void *cx) , позволяющая расшифровывать блок данных, подготовленных PK11SDR_Encrypt, которая и поможет расшифровать учетные данные.
А что делать с Chrome? Здесь используется GNOME-keyring, предназначенный для безопасного хранения информации — имен пользователей и паролей. Соответственно, вызвав набор определенных методов, можно получить необходимые учетные данные (расписывать методы не имеет смысла, это можно посмотреть в коде).
Заключение
Рассказ можно продолжать и продолжать, но, к сожалению, даже электронный формат статьи не позволит нам впихнуть всю информацию в разумный объем. Поэтому, чтобы не получился очередной конкурент «Войны и мира», на этом мы закончим. Для тех, кому интересно более глубоко окунуться в творение Hacking Team, приводим ссылочку на репозиторий Hacked Team на GitHub.
Но даже из того материала, который здесь описан, понятно, что Galileo — полноценный коммерческий продукт для слежки за пользователями, работающий на большинстве операционных систем. К сожалению (а для нас — к счастью), разработчикам платформы не повезло, и все сорцы утекли в Сеть. Ну а нам было бы грех не воспользоваться таким шансом и не покопаться в коде :).