Мы в JSOC CERT ежедневно сталкиваемся с событиями из разных песочниц, которые функционируют в составе AntiAPT-решений наших заказчиков и пропускают через себя тысячи файлов из web- и почтового трафика. Стоит отметить, что современные Sandbox-системы в своем развитии ушли намного дальше, чем простой перехват системных вызовов в Kernel Mode и API-функций в User Mode. Все чаще в них используются собственный гипервизор, система эмуляции пользовательской активности, динамическая инструментация, хэширование и кластеризация по участкам кода, анализ покрытия кода и т.д. Такое разнообразие технологий создает иллюзию, что если какой-то файл не отработал в песочнице и не показал свое «истинное лицо», то это наверняка APT или инновационная технология обнаружения виртуального окружения, о котором ИБ-сообществу еще не известно. Но…


Так как мы не знаем внутренних особенностей работы коммерческих песочниц, в некоторых случаях мы делаем double-check – вручную анализируем сэмплы, прошедшие проверку. За последнее время мы несколько раз столкнулись с тем, что некоторые коммерческие песочницы (по объективным причинам мы не можем сказать, какие) при динамическом анализе не детектировали определённые вредоносные файлы и, если статический анализатор тоже молчал, файл и вовсе пропускался.

Проверку в песочнице удалось обойти таким известным семействам вредоносного ПО, как Pony, Loki и Hawkeye. Объединяло их лишь одно — они были накрыты упаковщиком, написанным на Visual Basic.

Учитывая, что эти семейства ВПО уже давно не являются чем-то новым, «положительный» вердикт песочниц весьма удручает. Поэтому мы решили описать общий принцип работы этого упаковщика и наблюдения, сделанные нами на протяжении некоторого времени.
Общая схема работы упаковщика условно разделена на 4 стадии и изображена на схеме ниже.



Точка входа вредоносного файла выглядит типично для приложений на Visual Basic:



Мы встречали различные варианты данного упаковщика, и код VB Wrapper часто менялся, но выполняемая задача оставалась той же: передать управление коду Stage 1. В более ранних образцах передача управления производилась посредством API-функций класса Enum* (например, EnumWindows, EnumCalendarInfo и т.д), у которых в качестве параметра указывался адрес Stage 1 кода. В последнее время наблюдаем, что управление передается напрямую.

1 этап


Управление получает код Stage 1. Данный код не зашифрован, но обфусцирован. Методы обфускации варьируются от сэмпла к сэмплу, но общий алгоритм работы не меняется:

  1. Цикл с множеством (в том числе и мусорных) инструкций, который вырабатывает необходимый для расшифровки кода Stage 2 ключ. Особенность этого фрагмента кода заключается в том, что там отсутствуют Sleep-функции, но за счет большого количества итераций его выполнение занимает в среднем 1-2 минуты.
  2. Расшифровка (обычный XOR) и передача управления коду Stage 2.

На скриншоте ниже приведены примеры используемых методов обфускации:



2 этап


Основной задачей кода в Stage 2 является проверка окружения и реализация методов антиотладки. Некоторые участки кода зашифрованы (расшифровываются перед выполнением, а после – зашифровываются обратно все тем же алгоритмом XOR) для затруднения детектирования по сигнатурам. После расшифровки видны характерные признаки, по которым код Stage 2 можно узнать при ручном анализе.



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

1) GetTickCount + Sleep


Берется текущая метка времени, вызывается Sleep на 2 секунды, после чего сразу же берется еще одна метка времени.

После этого проверяется разница между метками (на самом ли деле прошло 2 секунды).



2) SetErrorMode


Проверяется правильность работы API-вызова SetErrorMode. Функция вызывается два раза подряд с параметрами 0x800 и 0x0, после чего результат второго вызова проверяется: он должен быть равен 0x800.



3) SetLastError


Сначала вызывается SetLastError с параметром 0x5, после чего проверяется, что значение Last error code в TEB правильно установилось (то есть, равно 0x5).



4) Проверка движения курсора


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



5) DbgBreakPoint и DbgUiRemoteBreakin


Данные функции модифицируются, чтобы помешать отладчику подсоединиться к процессу.


Техника


Комментарий


GetTickCount + Sleep


Проверка временных меток


SetErrorMode


Проверка пр��вильной работы функции


SetLastError


Проверка правильной работы функции


GetCursorPos


Проверка движения курсора


DbgBreakPoint


Модификация функции для запрета присоединения отладчика


DbgUiRemoteBreakin


Модификация функции для запрета присоединения отладчика


Hook Deletion


Восстанавливаются первые 5 байт функций в ntdll.dll на случай, если там стоят хуки


NtSetInformationThread


Параметр 0x11 (ThreadHideFromDebugger)


GetThreadContext + check DR


Проверяются отладочные регистры DR0-DR3, DR6, DR7


Check breakpoints


Проверяются инструкции INT3 (0xCC), int 3 (0xCD 0x03) и ud2 (0x0F 0x0B) в начале некоторых функций


cpuid (EAX=0x0)


Проверяются регистры EAX, ECX, EDX


cpuid (EAX=0x40000000)


Проверяются регистры EAX, ECX, EDX


cpuid (EAX=0x1)


Проверяется 31-й бит ECX


PEB (BeingDebugged)


Проверяется значение 0x1


PEB (NtGlobalFlag)


Проверяется значение 0x70


NtQueryInformationProcess


Вызывается с флагами ProcessDebugPort (0x7), ProcessDebugFlags (0x1F), ProcessDebugObjectHandle (0x1E)


Process Name Check


Проверяются строки «sample», ��sandbox», «virus», «malware», «self.»



В случае выполнения всех техник этапа 2 происходит проверка командной строки на соответствие специальному формату. Если проверка не проходит, то выполняются следующие действия:

1) Вызывается функция CreateProcess с флагом CREATE_SUSPENDED для перезапуска текущего процесса. При этом командная строка имеет необходимый формат.
2) С помощью функций GetContextThread и SetContextThread точка входа изменяется на новую, которая находится в коде Stage 1.
3) Повторяются этапы 1 и 2 (включая длительный цикл и все проверки). В этот раз проверка командной строки проходит успешно и процесс переходит к следующему этапу.

3 этап


На этом этапе расшифровывается тело основного вируса и выполняется техника Process Hollowing на текущий процесс, после чего управление передается на точку входа основного вируса.

Lesson Learned


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

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