Как вредоносы обходят песочницы с помощью Visual Basic
Мы в JSOC CERT ежедневно сталкиваемся с событиями из разных песочниц, которые функционируют в составе AntiAPT-решений наших заказчиков и пропускают через себя тысячи файлов из web- и почтового трафика. Стоит отметить, что современные Sandbox-системы в своем развитии ушли намного дальше, чем простой перехват системных вызовов в Kernel Mode и API-функций в User Mode. Все чаще в них используются собственный гипервизор, система эмуляции пользовательской активности, динамическая инструментация, хэширование и кластеризация по участкам кода, анализ покрытия кода и т.д. Такое разнообразие технологий создает иллюзию, что если какой-то файл не отработал в песочнице и не показал свое «истинное лицо», то это наверняка APT или инновационная технология обнаружения виртуального окружения, о котором ИБ-сообществу еще не известно. Но…
Так как мы не знаем внутренних особенностей работы коммерческих песочниц, в некоторых случаях мы делаем double-check – вручную анализируем сэмплы, прошедшие проверку. За последнее время мы несколько раз столкнулись с тем, что некоторые коммерческие песочницы (по объективным причинам мы не можем сказать, какие) при динамическом анализе не детектировали определённые вредоносные файлы и, если статический анализатор тоже молчал, файл и вовсе пропускался.
Объединяло их лишь одно — они были накрыты упаковщиком, написанным на Visual Basic. Проверку в песочнице удалось обойти таким известным семействам вредоносного ПО, как Pony, Loki и Hawkeye.
Поэтому мы решили описать общий принцип работы этого упаковщика и наблюдения, сделанные нами на протяжении некоторого времени.
Общая схема работы упаковщика условно разделена на 4 стадии и изображена на схеме ниже. Учитывая, что эти семейства ВПО уже давно не являются чем-то новым, «положительный» вердикт песочниц весьма удручает.
Точка входа вредоносного файла выглядит типично для приложений на Visual Basic:
В более ранних образцах передача управления производилась посредством API-функций класса Enum* (например, EnumWindows, EnumCalendarInfo и т.д), у которых в качестве параметра указывался адрес Stage 1 кода. Мы встречали различные варианты данного упаковщика, и код VB Wrapper часто менялся, но выполняемая задача оставалась той же: передать управление коду Stage 1. В последнее время наблюдаем, что управление передается напрямую.
1 этап
Управление получает код Stage 1. Данный код не зашифрован, но обфусцирован. Методы обфускации варьируются от сэмпла к сэмплу, но общий алгоритм работы не меняется:
- Цикл с множеством (в том числе и мусорных) инструкций, который вырабатывает необходимый для расшифровки кода Stage 2 ключ. Особенность этого фрагмента кода заключается в том, что там отсутствуют Sleep-функции, но за счет большого количества итераций его выполнение занимает в среднем 1-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 происходит проверка командной строки на соответствие специальному формату. Если проверка не проходит, то выполняются следующие действия:
При этом командная строка имеет необходимый формат.
2) С помощью функций GetContextThread и SetContextThread точка входа изменяется на новую, которая находится в коде Stage 1.
3) Повторяются этапы 1 и 2 (включая длительный цикл и все проверки). 1) Вызывается функция CreateProcess с флагом CREATE_SUSPENDED для перезапуска текущего процесса. В этот раз проверка командной строки проходит успешно и процесс переходит к следующему этапу.
3 этап
На этом этапе расшифровывается тело основного вируса и выполняется техника Process Hollowing на текущий процесс, после чего управление передается на точку входа основного вируса.
Lesson Learned
Точно сказать, что в данном случае вызывает проблемы у той или иной песочницы мы не можем, но хочется верить, что возможность использования вредоносным ПО техник, описанных в статье, давно предусмотрена вендорами, и проблема кроется лишь в длительной временной задержке на первом этапе работы упаковщика.
Так как нет гарантий, что сэмпл, который обошел песочницу, не будет иметь в арсенале пару техник по обходу антивируса, полагаться только на эту связку защитных решений нельзя. Несмотря на то, что современные песочницы в большинстве своем позиционируются как часть систем защиты от APT-атак, наши наблюдения говорят о том, что даже широко известные сообществу вредоносные семейства проникают внутрь инфраструктуры с завидным постоянством. В таких случаях обеспечить своевременное реагирование и минимизацию потенциального ущерба может правильно выстроенный процесс мониторинга, включающий в себя события информационной безопасности с конечных хостов.