Subatomic is a medium difficulty Sherlock where you are tasked with analysing an electron focused malware sample on behalf of the Forela organisation due to the compromise of a Discord account. You will be tasked with answering questions directly relevant to the analysis of a sample currently found in the wild.
Warning:
This is a warning that this Sherlock includes software that is going to interact with your computer and files. This software has been intentionally included for educational purposes and is NOT intended to be executed or used otherwise. Always handle such files in isolated, controlled, and secure environments.
Once the Sherlock zip has been unzipped, you will find a DANGER.txt file. Please read this to proceed.
The sample is an NSIS installer carrying a signed Electron application that poses as a Windows update tool. Inside the bundled app.asar sits a heavily obfuscated app.js whose real logic is an AES-encrypted stage decrypted at runtime. That stage fingerprints the host, bails out on known sandbox hostnames and VM processes, resolves the victim’s public IP, and registers with a hard-coded C2 API using a per-victim ID sent with every request. It then steals browser cookies, including Firefox’s, and rewrites a Discord desktop module so the client itself becomes the implant. A fallback routine pulls a replacement cmd.exe from the C2 into the user profile if the system one is missing. The whole chain was recovered statically; nothing was executed.
DANGER.txt → extract into an empty directory7z, pefile, openssl pkcs77zpackage.json and app.js → asar extractTimestamp note: No timestamps are needed in this case, every answer is a string read from the sample.
The installer is a PE, so the import hash comes straight from its import table. pefile computes it without loading anything.
python3 -I -c "import pefile,sys;print(pefile.PE(sys.argv[1]).get_imphash())" nsis-installer.exe
Pull the Authenticode blob out of the PE security directory and parse it as PKCS#7. The SpcSpOpusInfo attribute holds the program name as a BMPString, which strings misses because it is UTF-16, so decode it from the DER directly.
openssl pkcs7 -inform DER -in sig.der -print
Tip: Read the
BMPStringfrom the DER with a short script rather than eyeballing hex; the UTF-16 padding makesstringsoutput look empty.
NSIS installers register an uninstall entry under Software\Microsoft\Windows\CurrentVersion\Uninstall\<GUID>, and that GUID is the installation identifier. The 7-Zip build used here (v26) did not emit the [NSIS].nsi script, so the compressed script header was inflated by hand (raw Deflate) and searched for GUIDs. The one sitting after the Uninstall key path is the answer.
python3 -I -c "import zlib,re,sys;d=zlib.decompressobj(-15).decompress(open(sys.argv[1],'rb').read());print(re.findall(rb'Uninstall\\\\[{]?([0-9a-fA-F-]{36})',d))" nsis_header_raw.bin
Gotcha: The header is not LZMA here. Trying LZMA first returns nothing; it is plain Deflate.
The installer unpacks to app-32.7z, which holds the Electron app.asar. Extracting the archive exposes package.json and the obfuscated app.js next to it. The license field is in the manifest.
7z x nsis-installer.exe -o ex && 7z x ex/app-32.7z -o app && npx asar extract app/resources/app.asar asar && grep license asar/package.json
app.js is obfuscated with a string-table decoder, and webcrack only partly cleaned it. Rather than run the sample, only the decoder prelude was lifted into a standalone script to dump the string table, and that same prelude was then used to AES-decrypt the embedded stage (the key and IV parts come from the decoded strings). The decrypted payload.js is readable and contains the sandbox hostname blocklist.
node dec3.js asar/app.js > payload.js
grep -n -i 'arch' payload.js
The VM check is a list of process names in the decrypted stage. Dump the list and count duplicates; one VMware tools process appears twice.
grep -n -i 'vmware' payload.js
Search the decrypted stage for outbound HTTP calls. The victim’s public IP and geolocation are fetched from a third-party lookup service before the first C2 check-in.
grep -n -o -E "https?://[^'\" )]+" payload.js | sort -u
The stage holds an options object that carries the C2 configuration, including an api property. The host in that URL is the C2 domain.
grep -n -A6 'options' payload.js
The same options.api value from Q5 is the full URL, path included. The trailing slash is part of it.
grep -n 'api' payload.js
Follow the C2 request helper in payload.js. Every request builds its headers with the configured user_id under a fixed key name.
grep -n 'user_id' payload.js
checkCmdInstallation tests for the system cmd.exe. If it is missing, the function downloads a file from the C2 and calls writeFileSync to a path built from USERPROFILE.
grep -n -A12 'checkCmdInstallation' payload.js
getFirefoxCookies shells out with execSync to find the profile’s cookie database, since Firefox profile folders have random names.
grep -n -B2 -A6 'getFirefoxCookies' payload.js
The injection routine overwrites a file inside the Discord desktop install under modules\. The module folder name carries a numeric suffix, which is what the answer needs, and the file it replaces is index.js.
grep -n -i 'discord_desktop_core' payload.js
Gotcha: The module is
discord_desktop_core-1, notdiscord_desktop_core. Submitting the bare name from memory of the common Discord injector fails; copy the folder name as written in the sample.
| Phase | Action |
|---|---|
| Execution | Signed NSIS installer, presenting as a Windows update tool, drops an Electron app |
| Defense Evasion | Obfuscated app.js decrypts an AES-encrypted stage at runtime |
| Defense Evasion | Exits on known sandbox hostnames and VM processes |
| Discovery | Resolves the victim’s public IP through a lookup service |
| Command and Control | Registers with the C2 API, sending a per-victim ID in the headers of every request |
| Command and Control | Pulls a replacement cmd.exe from the C2 if the system one is missing |
| Credential Access | Locates and reads Firefox cookies alongside other browser data |
| Persistence | Overwrites index.js in the Discord desktop core module |
| Type | Value |
|---|---|
| Imphash | b34f154ec913d2d2c435cbd644e91687 |
| Install GUID | cfbc383d-9aa0-5771-9485-7b806e8442d5 |
| Domain (C2) | illitmagnetic[.]site |
| URL (C2 API) | hxxps[://]illitmagnetic[.]site/api/ |
| URL (IP lookup) | hxxps[://]ipinfo[.]io/json |
| Request header | duvet_user |
| File | %USERPROFILE%\Documents\cmd.exe |
| File (tampered) | modules\discord_desktop_core-1\index.js |
| Technique | ID | Description |
|---|---|---|
| Obfuscated Files or Information | T1027 | String-table obfuscation and an AES-encrypted stage inside app.js |
| Virtualization/Sandbox Evasion: System Checks | T1497.001 | Hostname blocklist and VM process list that terminate execution |
| System Network Configuration Discovery | T1016 | Public IP lookup through ipinfo.io |
| Application Layer Protocol: Web Protocols | T1071.001 | HTTPS check-ins to the C2 API with a per-victim header |
| Ingress Tool Transfer | T1105 | Replacement cmd.exe fetched from the C2 into the user profile |
| Steal Web Session Cookie | T1539 | Browser cookie theft, including Firefox via cookies.sqlite |
| Credentials from Password Stores: Credentials from Web Browsers | T1555.003 | Browser data targeted alongside Discord |
| Compromise Host Software Binary | T1554 | index.js in the Discord desktop core module overwritten |
A valid signature says who signed the installer, not what is in it. The installer carried an Authenticode signature with a plausible program name. Treat the signer, the SpcSpOpusInfo name and the actual product as three things to compare, and alert when a “Windows Update” style name comes from an NSIS installer.
Electron apps hide their logic in app.asar. Scanning the installer finds nothing useful; the manifest, the obfuscated script and the encrypted stage are one layer down. Unpack app.asar in triage and review package.json and the entry script before trusting the app.
Hard-coded C2 values make cheap blocks. The C2 domain, the API path and the distinctive per-victim header key are all constants in the decrypted stage. Hunt for the header key in proxy logs and block the domain.
Discord client tampering is a persistence signal. Modifying index.js under modules\discord_desktop_core-* makes the legitimate client run attacker code on every launch. File integrity monitoring on the Discord install directory catches it, and cleanup means reinstalling the client and rotating the account’s tokens.
Dropping cmd.exe into the Documents folder is an easy detection. Any cmd.exe outside System32 and SysWOW64, especially one written by a user-mode Electron process, should alert.
An Electron-wrapped stealer reached the Discord account by living inside a signed installer, an obfuscated script and a runtime-decrypted stage, and persisted by rewriting the Discord client itself. None of it needed execution to read; the sharpest lesson is that the C2, the victim identifier and the cleanup target were all plain constants once the layers were peeled.