Это .exe файл

На обычном NAS-е работает стандартный стек *arr приложений. Всё обычно функционирует нормально, но однажды на дашборде появилось предупреждение.

Новый эпизод South Park застрял в очереди загрузки с ошибкой. На панели NAS было видно имя файла с расширением .exe.

Кто-то взял название эпизода South Park, добавил .exe и загрузил его на публичные трекеры с 2 392 сидерами. Кто вообще выкладывает эпизоды в виде исполняемого файла?

Sonarr, конечно же, подхватила его. Потом он сидел там и пытался импортировать каждые 90 секунд:

Import failed, path does not exist or is not accessible by Sonarr:
/data/downloads/torrents/South Park S29E03 1080p WEB H264-MeGusta.exe

Даже не папка с поддельным .mkv и "инсталлятором кодека" рядом, как в добрые старые времена. Просто голый .exe. Что за время такое.

qBittorrent отметил его как "завершённый" через две секунды после добавления с total_downloaded: 0, так что на диск ничего не попало. Почему — неизвестно. И всё равно спасибо qBit.

Файл добавили в чёрный список Sonarr, что удалило его из qBittorrent и остановило повторные загрузки того же малвера.

А потом загрузили его снова намеренно. Потому что разозлились и захотели узнать, что там внутри.

887 МБ пустоты

Это настоящий PE, x64, с GUI. Сам программный код весит всего 118 КБ.

pe end 0x1ce00
overlay 886653736 bytes

Остальные 886 МБ — это просто случайный мусор, приклеенный в конец. Зачем? Потому что многие антивирусные сканеры сдаются перед файлами такого размера.

Информация о версии говорит:

CompanyName Freeware Developers
FileDescription prepares application data
OriginalFilename SetupHelper.exe

"Freeware Developers" — да ладно.

118 КБ откусили от начала и бросили в IDA, потому что гигабайт мусора никто загружать не будет. Оказалось, что весь код покрыт такими конструкциями:

dword_14000C020 ^= 0xF2C2EAC0;
v34 = 0;
dword_14000C020 ^= 0xF2C2EAC0;

XOR глобальную переменную. XOR её обратно. Ничего не делай. Сотни раз подряд. Это нужно только чтобы выход декомпилятора выглядел как говно — и да, выглядит.

Если убрать всю эту обфускацию, логика укладывается в шесть шагов:

  1. Открыть собственный файл.
  2. Прочитать последние 64 байта.
  3. Использовать их для поиска и расшифровки payload, спрятанного в мусоре.
  4. Вручную смапить его как PE в памяти.
  5. Указать PEB->ImageBaseAddress на него.
  6. Прыгнуть туда.

Ничего не трогает диск. А обфускация добавлена для шести шагов логики.

Footer

Последние 64 байта файла — это весь конфиг:

Смещение Размер Описание
0x00 32 байта ключ, XOR-ен с 32-байтовой маской в .data
0x20 12 байт ChaCha20 nonce
0x2C 4 байта размер payload ^ fnv1a(маска)
0x30 4 байта (флаги | padding << 8) ^ fnv1a(маска)
0x34 8 байт размер payload ^ fnv1a(маска)
0x3C 4 байта fnv1a(маска)

Это ChaCha20. Константы спрятаны, построены из глобалей, но в итоге всё равно выглядят как expand 32-byte k. Не спрячешь ChaCha20 от того, кто её видел.

flags = 3 включает проверку на отладчик (PEB->BeingDebugged, запросы к отладочному порту и стандартные трюки) и оценку антисанбокса. Если срабатывает любой из них, программа просто тихо завершается.

Для песочницы это выглядит как обычное приложение, которое открыло файл и вышло.

Но ключ буквально находится внутри файла. (????) Даже запускать его не нужно:

mask = bytes.fromhex("56951de331cd7f254bf1931533dd82bc3c8bf542f0d1ed5736a40f394c51d0a1")
h = fnv1a(mask)
size = u64(footer, 0x34) ^ (h | h << 32)
fl = h ^ u32(footer, 0x30)
pad = fl >> 8
key = xor(mask, footer[:32])
nonce = footer[32:44]
payload = chacha20(key, nonce, data[-64 - pad - size : -64 - pad])

И вот — второй PE, 929 КБ, MSVC, C++20, bundled zlib.

Вся эта противоанализная муть, а дюжина строк Python просто снимает обфускацию.

Неудивительно, это стилер

Строки зашифрованы, импорты сведены к CRT, всё разрешается во время выполнения.

И потом WinMain просто выплёвывает все модули по названиям в открытом виде:

osinfo
browsers
telegram
wallets
discord
steam
clipboard
remoteaccess
credentialmanager
authy
notes
obs
ssh
email
games
filegrabber

Для каждой строки — своя функция:

v5 = _mm_xor_ps(v6, v5);
v7 = { 1924987977, 1270327144, 877876327, 271431577 };
v8 = { 641333755, -1942364857, ... };
sub_140080F40(&v5, v7, v8, 0x20);

Два значения XOR-ены в ключ, IV, шифротекст, длина. sub_140080F40 — это AES, с S-box спрятанным за RC4, потому что одного слоя крипто, видимо, было мало.

Попробовали стандартные режимы на первом:

cbc  b'w\x1c\xe9\xc6\xa0y\x96\xa4...'
ctr  b'guid=\x00\x00\x00...'

AES-128-CTR. Свой ключ для каждой строки. 672 сайта вызовов.

Вручную это не делается.

Поэтому вытащили каждый сайт вызова из IDA, эмулировали в Unicorn всего несколько инструкций перед вызовом, достали ключ, IV и шифротекст из регистров, расшифровали в Python. 601 из 672 расшифровалась чисто.

Остальное закопано в более крупные функции, но честно говоря, 601 — вполне достаточно.

Хочет буквально всё

  • Браузеры: Chrome, Edge, Brave, Opera, Opera GX, Vivaldi, Firefox. Пароли, куки, автозаполнение, и даже code для нового Chrome app bound encryption.
  • Крипто-кошельки: Exodus, Electrum, Atomic, Guarda, Coinomi, Ledger Live, Trezor Suite, Wasabi, Sparrow, Monero, Zcash, Armory, плюс расширения-кошельки.
  • Менеджеры паролей: KeePass, KeePassXC, Bitwarden, 1Password, NordPass, RoboForm, Windows Credential Manager.
  • Аккаунты: Telegram, Discord токены, Steam, Roblox куки, Minecraft, Authy.
  • Девелоперское: .ssh, .aws, .azure, gcloud, .kube, .docker.
  • Удалённый доступ: AnyDesk, TeamViewer, WinSCP, FileZilla, mRemoteNG, RDCMan.
  • Остальное: Outlook, Thunderbird, конфиги VPN, настройки OBS, Sticky Notes, документы на рабочем столе.

Даже ваши Sticky Notes грабит — для ЧЕГО????

Всё архивируется встроенным zlib и отправляется.

YouTube канал

Нет хардкода сервера, вместо этого расшифровывает:

www.youtube.com/channel/UC18vFC1xgebddr_7NsvgBYA
gist.githubusercontent.com/jasonterra69/48d9e264c05843f0cdc302143b4400c8/raw
[[
]]
ZhxchsdfhSGdfgsgdfAJsakdaksdk2364236AH

YouTube канал, GitHub gist, две скобки и ключ, в котором кто-то явно прыгал по клавиатуре.

YouTube канал называется "bubblegym". Создан 14 сентября 2026:

YouTube канал bubblegym

Base64 разворачиваешь, XOR-ишь с прыгом по клавиатуре, получаешь адрес:

b = base64.b64decode("OA0dDQ0HFBQHJyIHEkkQCAlJLS0JTgwAAB8S")
k = b"ZhxchsdfhSGdfgsgdfAJsakdaksdk2364236AH"
bytes(x ^ k[i % len(k)] for i, x in enumerate(b))
beenetprotect[dot]com/lgz/gdata

Этот же адрес также захардкодирован в бинарнике как fallback??????

Весь смысл dead drop в том, чтобы менять серверы без того, чтобы адрес был в малваре. Но адрес там есть, так какой смысл фетчить с Gist и YouTube????

Opsec: Cloudflare Registrar

Domain Name: BEENETPROTECT.COM
Creation Date: 2026-09-14T06:21:54Z
Registrar: Cloudflare, Inc.
Name Server: DONALD.NS.CLOUDFLARE.COM
Name Server: JULISSA.NS.CLOUDFLARE.COM

Зарегистрирован 14 сентября. В день создания "игрового канала". Сборки от 6 октября, торрент от 8 октября.

PSA для пользователей *arr

Если Sonarr или Radarr работают с публичными индексерами — это ударит и вас. Sonarr .exe не запустит, это не проблема.

Проблема в соседе по комнате, в родителе, или в вас в 3 ночи, когда вы двойным кликом откроете это в папке загрузок. (ПОЖАЛУЙСТА, НЕ запускайте этот малвер)

Пять минут настройки:

  • qBittorrent: добавить *.exe, *.scr, *.lnk, *.bat, *.cmd в "Excluded file names" — просто отказывать в загрузке.
  • Sonarr / Radarr: blacklist с "Remove from client" при виде одного из этих.
  • Prowlarr: если трекер постоянно раздаёт такое дерьмо, понизить рейтинг или удалить.

А тому, кто это делал: вы потратили недели или кучу токенов на ChaCha20, AES-CTR, хеширование API во время выполнения, анти-санбокс скоринг, обход Chrome app bound encryption, просто чтобы сделать паршивый скид-малвар.

Найди нормальную работу.