Iemand op torrent-sites bleef mijn Sonarr saboteren

Ik draai de gebruikelijke *arr-stack op mijn NAS. Normaal gesproken werkt dit prima, totdat ik een waarschuwing op mijn dashboard zag. De nieuwe aflevering van South Park zat vast in de wachtrij.

Wie trapt hier in het jaar 2026 eigenlijk nog in? Het is een EXE-bestand.

Laat me heel duidelijk zijn over wat hier aan de hand is: iemand heeft een bestandsnaam van een South Park-aflevering genomen, er .exe achter gezet en dit geüpload naar publieke trackers met 2.392 seeders. Wie uploadt er in vredesnaam afleveringen als EXE-bestand?

En Sonarr heeft het natuurlijk gewoon opgepikt. Vervolgens bleef het programma elke 90 seconden proberen het bestand te importeren:

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

Het is niet eens een map met een nep .mkv en een "codec-installer" ernaast, zoals in de goede oude tijd. Gewoon direct het EXE-bestand. Meent dit serieus?

qBittorrent markeerde het bestand twee seconden na het toevoegen als "voltooid" met total_downloaded: 0, dus er is feitelijk niets op de schijf beland. Waarom dat zo is, heb ik geen idee en dat interesseert me ook niet. Bedankt, qBit.

Ik heb het bestand in Sonarr op de bloklijst gezet, wat het uit qBittorrent verwijdert en voorkomt dat dezelfde junk-malware opnieuw wordt gedownload. Daarna heb ik het echter expres opnieuw gedownload. Ik was inmiddels zo boos dat ik wilde weten wat er precies in zat.

887 MB aan niets

Het is een echt PE-bestand (Portable Executable), x64 GUI. Het eigenlijke programma is slechts 118 KB:

  • PE end: 0x1ce00
  • Overlay: 886.653.736 bytes

De overige 886 MB bestaat uit willekeurige rommel die aan het einde is geplakt. Waarom? Omdat veel scanners simpelweg opgeven bij bestanden van deze omvang.

De versie-informatie vermeldt:

  • CompanyName: Freeware Developers
  • FileDescription: prepares application data
  • OriginalFilename: SetupHelper.exe

"Freeware Developers," mijn schoen.

Ik heb de eerste 118 KB eraf gesneden en in IDA gegooid, omdat ik geen gigabyte aan onzin in mijn tools wil laden. De volledige code is vervolgens bezaaid met dit soort instructies:

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

Een globale variabele XOR-en, en hem vervolgens weer terug XOR-en. Doen maar niets. Honderden keren achter elkaar. Dit bestaat puur om de output van de decompiler onleesbaar te maken, en dat is ze gelukt. Gefeliciteerd.

Als je al die ruis verwijdert, bestaat de logica uit slechts zes stappen:

  1. Het eigen bestand openen.
  2. De laatste 64 bytes lezen.
  3. Deze gebruiken om een payload te vinden en te ontsleutelen die verborgen zit in de "junk".
  4. Deze handmatig in het geheugen mappen als een PE.
  5. PEB->ImageBaseAddress hiernaar laten wijzen.
  6. Ernaar springen (uitvoeren).

Niets raakt de schijf. Al die obfuscatie voor slechts zes stappen.

De configuratie: ChaCha20

De laatste 64 bytes van het bestand vormen de volledige configuratie:

OffsetGrootteBeschrijving
0x0032 byteskey, XORed met een 32-byte mask in .data
0x2012 bytesChaCha20 nonce
0x2C4 bytespayload size ^ fnv1a(mask)
0x304 bytes(flags \padding << 8) ^ fnv1a(mask)
0x348 bytespayload size ^ fnv1a(mask)
0x3C4 bytesfnv1a(mask)

Er wordt gebruikgemaakt van ChaCha20. De constanten zijn verborgen door ze op te bouwen uit globale variabelen, maar ze komen nog steeds uit op de standaard 32-byte k-expansie. Je kunt ChaCha20 niet verbergen voor iemand die ChaCha20 kent.

De instelling flags = 3 activeert een anti-debug check (PEB->BeingDebugged, debug port queries, etc.) en een anti-sandbox score. Als een van beide wordt getriggerd, sluit het programma zichzelf stilzwijgend af. Een sandbox ziet dus een normale applicatie die een bestand opent en weer vertrekt.

Leuk, behalve dat de sleutel letterlijk in het bestand zit. Je hoeft het programma niet eens uit te voeren om dit te ontdekken:

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])

Hieruit komt een tweede PE tevoorschijn: 929 KB, MSVC, C++20, met zlib ingebouwd. Al die anti-analyse onzin, maar een dozijn regels Python strippen de obfuscatie direct weg.

Het resultaat: een 'Stealer'

Verrassing: het is een Stealer. De strings zijn versleuteld, imports zijn gereduceerd tot de CRT en alles wordt tijdens runtime opgelost. In WinMain worden vervolgens alle modules simpelweg per naam in platte tekst gedumpt:

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

Elke andere string heeft zijn eigen functie. Er wordt gebruikgemaakt van twee constanten die in een sleutel worden ge-XOR'd, een IV, ciphertext en lengte. De functie sub_140080F40 is AES, waarbij de S-box verborgen is achter RC4, omdat één laag encryptie blijkbaar niet "edgy" genoeg was.

Na het testen van standaard modi bleek het AES-128-CTR te zijn, met een unieke sleutel per string. Er zijn 672 aanroeplocaties. Dat ga ik niet handmatig doen. Daarom heb ik elke aanroeplocatie uit IDA gehaald, de instructies vlak voor de aanroep geëmuleerd in Unicorn, de sleutel, IV en ciphertext uit de registers gepakt en deze in Python ontsleuteld. 601 van de 672 strings kwamen schoon uit. De rest zit begraven in grotere functies, maar dat kon me eerlijk gezegd niet schelen; 601 was meer dan genoeg.

Wat het precies steelt

De malware wil letterlijk alles:

  • Browsers: Chrome, Edge, Brave, Opera, Opera GX, Vivaldi, Firefox. Wachtwoorden, cookies, autofill en code voor de nieuwe 'app bound encryption' van Chrome.
  • Crypto wallets: Exodus, Electrum, Atomic, Guarda, Coinomi, Ledger Live, Trezor Suite, Wasabi, Sparrow, Monero, Zcash, Armory, plus browser-extensies.
  • Wachtwoordmanagers: KeePass, KeePassXC, Bitwarden, 1Password, NordPass, RoboForm, Windows Credential Manager.
  • Accounts: Telegram, Discord tokens, Steam, Roblox cookies, Minecraft-accounts, Authy.
  • Dev-tools: .ssh, .aws, .azure, gcloud, .kube, .docker.
  • Remote access: AnyDesk, TeamViewer, WinSCP, FileZilla, mRemoteNG, RDCMan.
  • Overig: Outlook, Thunderbird, VPN-configuraties, OBS-instellingen, Sticky Notes en documenten op je bureaublad.

Waarom het zelfs je Sticky Notes pakt, is me een raadsel. Alles wordt gezipt met de ingebouwde zlib en verzonden.

De infrastructuur en C2-server

Er is geen hardcoded server. In plaats daarvan ontsleutelt de malware het volgende:

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

Een YouTube-kanaal, een GitHub Gist, twee haken en een sleutel die overduidelijk is ontstaan door willekeurig op het toetsenbord te rammen. Het YouTube-kanaal heet "bubblegym" en is aangemaakt op 14 september 2026.

Door de data Base64 te decoderen en te XOR-en met de toetsenbord-sleutel, krijg je het adres: beenetprotect[dot]com/lgz/gdata

Datzelfde adres staat overigens ook hardcoded in het binary als fallback. Ik dacht dat het hele punt van een 'dead drop' was dat je van server kunt wisselen zonder het adres in de malware te zetten. Maar als je het adres toch in de malware zet, wat is dan het nut van het ophalen via Gist en YouTube?

Opsec-niveau: Cloudflare Registrar

  • Domeinnaam: BEENETPROTECT.COM
  • Aanmaakdatum: 14-09-2026
  • Registrar: Cloudflare, Inc.

Het domein is op 14 september geregistreerd, dezelfde dag als het "gaming channel". De malware is gebouwd op 6 oktober en op 8 oktober op torrent-sites geplaatst.

Waarschuwing voor \*arr-gebruikers

Als je Sonarr of Radarr gebruikt met publieke indexers, zul je dit tegenkomen. Sonarr kan geen EXE-bestanden uitvoeren, dus daar zit het probleem niet.

Het probleem is je huisgenoot, je ouders, of jijzelf om 3 uur 's nachts die per ongeluk dubbelklikt op het bestand in de downloadsmap. Voer deze script-kiddie malware dus NIET uit.

Vijf minuten instellen voor veiligheid:

  • qBittorrent: voeg .exe, .scr, .lnk, .bat, *.cmd toe aan "Uitgesloten bestandsnamen" (Excluded file names). De client weigert ze dan simpelweg te downloaden.
  • Sonarr / Radarr: zet bestanden op de bloklijst met de optie "Remove from client" zodra je een van deze ziet.
  • Prowlarr: als een tracker herhaaldelijk deze troep aanbiedt, degradeer hem dan of verwijder hem volledig.

En tot degene die dit gemaakt heeft: je hebt waarschijnlijk weken gewerkt of een enorme hoeveelheid tokens verbruikt aan ChaCha20, AES-CTR, runtime API hashing, anti-sandbox scoring en Chrome app bound encryption bypasses, alleen maar om een waardeloos stukje script-kiddie malware te maken.

Zoek een baan.