Современный гипервизор как основа для “песочницы”

В сфере информационной безопасности “песочницы” используют для изоляции внешней (опасной) среды от внутренней (безопасной) и наоборот, для защиты от последствий использования уязвимостей, а также для анализа вредоносного кода. У нас в “Лаборатории Касперского” есть несколько “песочниц”, в том числе и Android Sandbox. Но сейчас мы хотим лишь рассказать об одной из них, которая, будучи адаптированной под нужды конкретного продукта, стала ядром платформы для защиты от целевых атак — Kaspersky Anti Targeted Attack Platform. Речь о системе анализа Windows-приложений, которая помогает нам автоматизировать анализ и детектирование вредоносного кода, проводить исследования и своевременно обнаруживать новейшие виды атак.

Реализовать “песочницу” для динамического анализа вредоносного кода можно по-разному. К примеру, использовать:

  • классическую эмуляцию, перехват функций в пользовательском пространстве и в пространстве ядра;
  • информацию от функций обратного вызова ядра и от различных фильтр-драйверов;
  • аппаратную виртуализацию.

А также можно совмещать все эти способы.

Практика показала, что реализовать полноценную эмуляцию — дорогостоящее удовольствие, требующее постоянной поддержки и доработок в эмуляции API функций, а также повышенное внимание к техникам уклонения от исполнения и техникам детектирования эмулятора. Что касается перехватов, то и они продержались недолго: вредоносное ПО стало обходить их довольно простыми методами, “научилось” определять их наличие и отказываться исполнять вредоносную составляющую.

Способы детектирования и обхода сплайсинга известны многие годы — достаточно проверить или протрассировать пролог популярных API-функций или построить свой собственный пролог, который позволит обойти перехват (этим способом пользуются крипторы и упаковщики). Кроме этого, сама технология довольно нестабильна в многопоточной среде. Также очевидно, что уровень изоляции вредоносного кода от перехватчиков в пользовательском пространстве практически стремится к нулю, так как модифицируется сама операционная система, а это слишком заметно.

Но и это ещё не всё. Для получения результатов исполнения API функции необходимо получить управление после её исполнения, обычно это делается перезаписью адреса возврата. Данный механизм также оказался нестабильным. Но самой большой головной болью стала попытка перенести подобный механизм на новые операционные системы.

Поэтому если какой-либо производитель защитных решений утверждает, что его “песочница” с использованием сплайсинга API функций и на событиях от ядра Windows “невероятна, уникальна, не детектируема и обеспечивает близкий к 100% результат”, мы рекомендуем его отправить обратно, в тот в каменный век, из которого он вышел. Возможно, этого производителя защитных решений устраивает такое положение вещей, но нас — нет.

Приняв во внимание вышеизложенные факты (и ещё некоторые другие), мы реализовали нашу “песочницу” на основе аппаратной виртуализации. На текущий момент такое решение является оптимальным с точки зрения баланса производительности, расширяемости и изоляции. Гипервизор обеспечивает хорошую степень изоляции гостевой виртуальной машины от хоста, позволяя контролировать CPU и RAM. При этом современные процессоры обеспечивают минимальное падение производительности при использовании виртуализации.

Инфраструктура

“Железо” для нашей “песочницы” закупалось в разные годы и продолжает закупаться по сей день, потому ее инфраструктура довольно разнообразна. На текущий момент у нас развёрнуто около 75 высокопроизводительных серверов — это четыре ноды в трёх дата-центрах, суммарно задействовано около 2500 vCPU. Мы используем разное “железо” начиная от систем M2 и блейд-серверов, заканчивая M5-системами под управлением Intel Xeon E5 с поддержкой необходимых нам технологий. Одновременно работают до 2000 виртуальных машин.

В день через весь сервис проходит до четырех миллионов объектов в пиках, в среднем — около двух миллионов.

Для выхода в интернет из “песочницы” используются около пятнадцати каналов, детали которых мы предпочтём не разглашать. При этом исходящий трафик с нод в пиках достигает 5 Гбит/с, среднее значение же — около 2 Гбит/с.

Внутреннее устройство

Наша “песочница” состоит из множества компонент, каждая из которых отвечает за определённые функции. Транспортная подсистема “общается” с внешним миром, получает команды извне и передаёт накопленную информацию. Есть подсистемы, которые отвечают за файловое и сетевое взаимодействие, следят за потоками/процессами и за обращением к реестру Windows. Подсистема логирования отвечает за сбор информации на входе и выходе API функций. В системе также присутствует компонент, который отвечает за эмуляцию действий пользователя. Кроме этого, мы предусмотрели возможность создавать и использовать плагины для расширения функциональности.

Преимуществом нашего решения является не только широкая функциональность, но и то, что систему логирования можно установить на любую операционную систему или на реальное железо. При этом образ гостевой ОС можно адаптировать под свои нужды.

Кроме этого, у наших аналитиков есть возможность создавать специальные подпрограммы для детектирования по накопленным артефактам, а также для проведения различных исследований. В том числе существуют подпрограммы, которые работают в реальном времени в самой “песочнице”.

Обработка объекта и артефакты

В зависимости от типа поступающего на обработку файла он “оборачивается” компонентом Task Processor в специальный пакет, который содержит дополнительную информацию: как запускать, какой образ операционной системы выбрать, сколько времени выделить на обработку и т.п.

Затем другой компонент, Task Executor, выполняет следующие действия:

  1. “Поднимает” виртуальную машину;
  2. Передаёт файл;
  3. Производит дополнительную настройку гостевой ОС;
  4. Исполняет файл;
  5. Ожидает окончания исполнения;
  6. Сканирует и/или передаёт накопленные артефакты.

Артефактами нашей “песочницы” являются:

  • Лог исполнения программы (вызовы всех API функций со всеми параметрами, а также некоторые события);
  • Дампы различных участков памяти, загруженных модулей и т.д.;
  • Всевозможные изменения файловой системы, а также системного реестра;
  • PCAP-файлы, содержащие сетевые данные;
  • Скриншоты.

Подсистема логирования

Подсистема логирования — центральный механизм нашей “песочницы”, который реализует технику неинвазивного перехвата вызываемых API функций и возврата из них. Это означает, что подсистема умеет “приостанавливать” работу потока исследуемого процесса в те моменты, когда он вызывает API функцию или возвращается из неё, а также синхронно обрабатывать это событие. Все это происходит без модификации кода.

Для каждой страницы виртуального адресного пространства мы вводим атрибут принадлежности этой страницы к известному модулю DLL (Known Module — KM). В каждый определённый момент времени для определённого потока исполняемыми являются страницы, у которых установлен атрибут KM, либо страницы, у которых он не установлен, но никогда одновременно. Получается, что при попытке сделать вызов API функции, управление передаётся на KM-страницу, которая в данный момент не является исполняемой в соответствии с правилом выше. Процессор генерирует исключение, что приводит к выходу в гипервизор и это событие обрабатывается. Всё тоже самое с точностью до наоборот происходит в момент, когда API функция возвращает управление.

В левой части картинки представлена память среднестатистического процесса: красным помечены те области, выполнение инструкций в которых запрещено, а зелёным те, где это разрешается. В правой части — тот же процесс в двух состояниях: исполнение разрешено в системных библиотеках или же где-то ещё, но никогда одновременно. Соответственно, если научиться “подкрашивать” в красный всё адресное пространство пользовательского режима в нужный момент времени, то можно ловить и возвраты из системных вызовов.

Всё это работает за счёт введения копий таблиц страниц оригинального адресного пространства, которые используются для трансляции виртуального адреса в физический. В одной из копий страницы с KM-атрибутом — исполняемые, без KM-атрибута — нет. В другой копии все наоборот. Каждая запись в такой таблице соответствует определённой странице виртуального адресного пространства и, среди прочего, имеет атрибут NX, который говорит процессору, может ли он исполнять инструкции на этой странице. Вышеописанное правило как раз и отвечает за содержимое этого атрибута в зависимости от копии и принадлежности этой страницы. Для того, чтобы содержать копии таблиц страниц в актуальном состоянии в подсистеме присутствует модуль, который синхронно реагирует на изменения оригинального адресного пространства и, в соответствии с нашими правилами, накладывает эти изменения на наши копии адресных пространств, при этом операционная система не знает о том, что она работает на копиях оригинального адресного пространства и для неё всё происходит прозрачно.

Противодействие уклонению

Современное вредоносное ПО использует всевозможные методы уклонения от исполнения участков кода, которые могут выявить вредоносную активность.

Наиболее часто применяемые техники:

  • Детектирование виртуальной среды исполнения (“песочница”, эмулятор, и так далее) по косвенным признакам;
  • “Таргетирование” исполнения: вредоносная активность проявляется только в том случае, если программа запустилась в правильной/нужной среде исполнения, в определённое время и т.п.

В случае обнаружения среды анализа вредоносным кодом может, как минимум, произойти следующее:

  • Мгновенное завершение работы;
  • Самоликвидация;
  • Исполнение бесполезного участка кода;
  • Исполнение безопасного участка кода;
  • Попытка компрометации известной системы анализа;
  • Другие варианты.

В случае несоответствия системы необходимым параметрам, вредоносная программа может выполнять всё вышеперечисленное, но, скорее всего, произойдёт самоликвидация, чтобы не оставлять лишних следов в системе.

Разработчики “песочниц” должны уделять повышенное внимание техникам уклонения, и мы не являемся исключением. Техники попадают к нам из различных источников: публичные выступления, статьи, инструменты с открытым кодом, например, Pafish, ну и конечно же, вредоносный код. Кроме постоянных доработок нашей “песочницы”, дополнительно мы реализовали автоматическую рандомизацию различных параметров гостевой среды, чтобы снизить процент отказа от исполнения.

Методы уклонения Vault 7

В результате утечки Vault 7 нам стала известна информация о потенциальном методе уклонения от исполнения в нашей “песочнице”:

“Троян Upclicker использует API SetWindowsHookExA с параметром WH_MOUSE_LL, ожидая пока пользователь отпустит левую кнопку мыши (WM_LBUTTONUP), а после выполняет вредоносную составляющую (внедряет свой код в Explorer.exe). “Песочница”, которая не имитирует действия мыши (вероятно, большинство из них), никогда не исполнит вредоносный код. Вероятно, этот метод эффективен против Касперского и других”.

Интересное предположение, которое мы тут же проверили. Мы реализовали консольное приложение (исходный код прилагается, проверяйте ваши “песочницы”) и совершенно не удивились тому, что функция ExecuteEvil() успешно исполнилась.

/*
    Copyright 2017 AO Kaspersky Lab. All Rights Reserved.
    Anti-Sandboxing: Wait for Mouse Click PoC: https://wikileaks.org/ciav7p1/cms/page_2621847.html
    RU: https://securelist.ru/a-modern-hypervisor-as-a-basis-for-a-sandbox/80739/
    EN: https://securelist.com/a-modern-hypervisor-as-a-basis-for-a-sandbox/81902/
*/
#include stdafx.h
#include <windows.h>
#include <iostream>
#include <thread>
#include <atomic>
HHOOK global_hook = nullptr;
std::atomic<bool> global_ready(true);
void ExecuteEvil() {
    std::cout << This will never be executed in Sandbox << std::endl;
    // TODO: add your EVIL code here
    UnhookWindowsHookEx(global_hook);
    ExitProcess(42);
}
LRESULT CALLBACK LowLevelMouseProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) {
    if ( nCode < 0 ) {
        return CallNextHookEx(nullptr, nCode, wParam, lParam);
    }
    if ( nCode == HC_ACTION && wParam == WM_LBUTTONUP && global_ready == true ) {
        global_ready = false;
        std::thread(ExecuteEvil).detach(); // execute EVIL thread detached
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
int _tmain(int argc, _TCHAR* argv[]) {
    FreeConsole(); // hide console window
    global_hook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, nullptr, 0);
    // emulate message queue
    MSG msg;
    while ( GetMessage(&msg, NULL, 0, 0) ) {
        Sleep(0);
    }
    return 0;
}

GitHub

Не удивились, потому что за эмуляцию действий пользователя в нашей “песочнице” отвечает отдельный компонент, действия которого не отличить от действий обычного пользователя. Данный компонент имеет общее поведение, а кроме этого, он “знает” популярные приложения и ведёт себя в этих приложениях как обычный пользователь, например, “читает” документы, открытые в Microsoft Word и устанавливает приложения, если запускается инсталлятор.

Эвристический поиск эксплойтов

Благодаря наличию системы плагинов мы можем бесконечно расширять возможности “песочницы”. Одним из таких плагинов является Exploit Checker — плагин, детектирующий типичную активность ранних фаз пост-эксплуатации. Детектируемые им события записываются в лог, а относящаяся к ним память сбрасывается на диск для последующего анализа.

Примеры некоторых событий Exploit Checker:

  • Эксплуатируемые исключения:
    • Нарушение DEP
    • Повреждение кучи
    • Запрещённая/привилегированная инструкция
    • и другие.
  • Исполнение на стеке;
  • Детектирование EoP;
  • Предетектирование Heap Spray;
  • Исполнение кода пользовательского пространства в Ring 0;
  • Изменение токена процесса.
  • и другие…

CVE-2015-2546

Давайте посмотрим на уязвимость CVE-2015-2545 с бесплатным приложением в виде CVE-2015-2546. Первой уязвимости подвержены Microsoft Office версий 2007 SP3, 2010 SP2, 2013 SP1 и 2013 RT SP1 — она позволяет исполнить код с использованием специально сконструированного EPS-файла. Вторая уязвимость позволяет исполнить код в режиме ядра. Обе уязвимости были использованы в целевой атаке группы Platinum (aka TwoForOne). Сначала использовалась уязвимость CVE-2015-2545 для того, чтобы исполнить код в процессе WINWORD.EXE, а потом уязвимость CVE-2015-2546, чтобы повысить привилегии до SYSTEM.

CVE-2015-2546 — это классическая уязвимость вида Use-After-Free (UAF). Результатом её эксплуатации является повышение привилегий процесса до SYSTEM. Вот на неё мы и обратим внимание, опустив детали исполнения первой уязвимости.

“Сдетонировав” специально сконструированный документ в нашей “песочнице”, мы получили общий лог выполнения, который был отфильтрован по событиям плагина Exploit Checker. Событий было получено достаточно много, поэтому приведём самые интересные — те, которые позволяют получить шеллкод уязвимости CVE-2015-2546 — код пользовательского пространства, исполняющийся в режиме ядра (для противодействия этой технике используется SMEP).

[…]
UserSpaceSupervisorCPL(“VA:0000000001FC29C0”,allocbase=0000000001FC0000,base=0000000001FC2000,size=4096(0x1000),dumpBase=0000000001FC2000,dumpid=0xD)
SecurityTokenChanged()
[…]
  1. Найдём дамп с ID = 0xD среди всех дампов памяти процесса FLTLDR.EXE;
  2. Базовый адрес участка памяти равен 0x1FC2000, а адрес кода расположен по адресу 0x1FC29C0;
  3. Смещение шеллкода будет равно 0x1FC29C0 — 0x1FC2000 = 0x9C


Шеллкод в одном из дампов памяти

Разумеется, в зависимости от вида уязвимости алгоритм поиска шеллкода будет варьироваться, но сути дела это не меняет.

Exploit Checker — это плагин к системе логирования, который поставляет дополнительные события, основанные на некоторой эвристике, в лог исполнения. Кроме этого, он собирает необходимые артефакты — дампы памяти, которые используются для дальнейшего анализа и детектирования.

BlackEnergy в “песочнице”

Мы уже рассказывали про атаку APT-группы BlackEnergy на Украине с использованием документов Microsoft Word. Краткое содержание анализа:

  1. Для атаки были использованы документы Microsoft Word с макросами;
  2. Макрос сбрасывает на диск файл vba_macro.exe— типичный дроппер BlackEnergy;
  3. vba_macro.exe сбрасывает на диск файл FONTCACHE.DAT, который является обычной библиотекой (DLL);
  4. Чтобы DLL файл исполнялся при каждом запуске системы, дроппер создаёт файл LNK в системной папке автозапуска;
  5. Троянец соединяется с C&C149.254.114.

Предлагаем взглянуть на небольшую часть лога исполнения, который мы получили “сдетонировав” вредоносный документ Microsoft Word в нашей “песочнице” под гостевой Windows 7 x64.

[0XXX] >> ShellExecuteExW (“[HIDDEN_DIR]\e15b36c2e394d599a8ab352159089dd2.doc”)
[…]

\Device\HarddiskVolumeZ\Program Files (x86)\Microsoft Office\Office14\WINWORD.EXE
&”%PROGRAM_FILES%\Microsoft Office\Office14\WINWORD.EXE&” /n &”[HIDDEN_DIR]\e15b36c2e394d599a8ab352159089dd2.doc&”

[…]
\Device\HarddiskVolumeZ\Program Files (x86)\Microsoft Office\Office14\WINWORD.EXE


\SystemRoot\System32\ntdll.dll


\SystemRoot\SysWOW64\ntdll.dll

[…] [0YYY] >> SetWindowTextW (0000000000050018,00000000001875BC -> “e15b36c2e394d599a8ab352159089dd2.doc [Compatibility Mode] — Microsoft Word”) => 00000000390A056C {0000}
[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe

[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe

[0YYY] << CloseHandle () [00000001] {0000}
[…] [0YYY] >> CreateProcessW (0000000000000000 -> (NULL),000000000047FEDC -> “%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe”,0000000000000000,0000000000000000,00000000,00000000,0000000000000000,0000000000000000 -> (NULL),00000000001883B0 -> (STARTUPINFOEXW*){(STARTUPINFOW){,,lpDesktop=0000000000000000 -> (NULL),lpTitle=0000000000000000 -> (NULL),,,,,,,,,wShowWindow=0001,,,,,},},00000000001883F4) => 000000000B87C2F8 {0000}


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe
%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe


\Users\[HIDDEN_USER]\AppData\Local\Temp\vba_macro.exe

[…] [0ZZZ] << SHGetFolderPathA (,,,,000000000018FCC0 -> “%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local”) [00000000] {0000}
[0ZZZ] >> CreateFileA (000000000018FCC0 -> “%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT”,40000000,00000000,0000000000000000,00000002,00000002,0000000000000000 -> (NULL)) => 0000000000421160 {0000}

\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT

[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT

[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT

[0ZZZ] << CloseHandle () [00000001] {0000}
[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\{C2F5139C-7918-4CE6-A17C-77B9290128D8}.lnk

[…]
\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\{C2F5139C-7918-4CE6-A17C-77B9290128D8}.lnk


\Device\HarddiskVolumeZ\Users\[HIDDEN_USER]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\{C2F5139C-7918-4CE6-A17C-77B9290128D8}.lnk

[…] [0ZZZ] >> ShellExecuteW (0000000000000000,000000000018FEC8 -> “open”,000000000018F8B0 -> “%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\{C2F5139C-7918-4CE6-A17C-77B9290128D8}.lnk”,0000000000000000 -> (NULL),0000000000000000 -> (NULL),00000000) => 000000000042195D {0000}
[…]

\Device\HarddiskVolumeZ\Windows\SysWOW64\rundll32.exe
&”%SYSTEM_ROOT%\Windows\System32\rundll32.exe&” &”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT&”,#1

[…] [0ZZZ] >> CreateProcessA (000000000018F334 -> “%SYSTEM_ROOT%\Windows\system32\cmd.exe”,000000000018EE20 -> “/s /c \”for /L %i in (1,1,100) do (attrib +h \”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\VBA_MA~1.EXE\” & del /A:h /F \”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\VBA_MA~1.EXE\” & ping localhost -n 2 & if not exist \”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT\” Exit 1)\””,0000000000000000,0000000000000000,00000000,08000000,0000000000000000,0000000000000000 -> (NULL),000000000018F848 -> (STARTUPINFOA*){cb=00000044,lpReserved=0000000000000000 -> (NULL),lpDesktop=0000000000000000 -> (NULL),lpTitle=0000000000000000 -> (NULL),dwX=00000000,dwY=00000000,dwXSize=00000000,dwYSize=00000000,dwXCountChars=00000000,dwYCountChars=00000000,dwFillAttribute=00000000,dwFlags=00000001,wShowWindow=0000,cbReserved2=0000,lpReserved2=0000000000000000,hStdInput=0000000000000000 -> (NULL),,},000000000018F88C) => 0000000000421666 {0000}


\Device\HarddiskVolumeZ\Windows\SysWOW64\cmd.exe
/s /c &”for /L %i in (1,1,100) do (attrib +h &”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\VBA_MA~1.EXE&” & del /A:h /F &”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\Temp\VBA_MA~1.EXE&” & ping localhost -n 2 & if not exist &”%SYSTEM_ROOT%\Users\[HIDDEN_USER]\AppData\Local\FONTCACHE.DAT&” Exit 1)&”

[…]

В результате исполнения вредоносного документа мы получили:

  • Лог вызова API функций во всех процессах, связанных с вредоносными действиями;
  • Карту памяти всех этих процессов, куда входят как загруженные модули, так и динамическая память;
  • Все изменения файловой системы;
  • Сетевые пакеты;
  • Скриншоты.

Информации более чем достаточно для детального анализа.

Выводы

Наша “песочница” для Windows-приложений — большой и сложный проект, существующий уже несколько лет. За это время система логирования показала свою эффективность, поэтому мы используем её не только во внутренней инфраструктуре, но и в Kaspersky Anti Targeted Attack Platform.

Несмотря на то, что использование гипервизора решило множество проблем с обнаружением “песочницы”, время от времени у злоумышленников появляются новые техники, поэтому мы пристально следим за ландшафтом угроз и своевременно обновляем кодовую базу.

Публикации на схожие темы

Leave a Reply

Your email address will not be published. Required fields are marked *