You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/blog/malware-analysis-brazilian-vbs-trojan.md
+39-57Lines changed: 39 additions & 57 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,14 @@
1
1
---
2
2
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"
4
4
pubDate: "2026-03-16"
5
5
featured: true
6
6
category: "Malware Analysis"
7
7
---
8
8
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.
10
10
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 !).
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.
69
69
70
70
---
71
71
72
72
## Stage 1 - The VBScript Dropper
73
73
74
74
### Overview
75
75
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.
**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.
96
92
97
93
### The Obfuscation Layer - `UhiSmA1()`
98
94
@@ -108,7 +104,7 @@ Function UhiSmA1(rpidscSPL2)
108
104
EndFunction
109
105
```
110
106
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.
112
108
113
109
### Deobfuscation
114
110
@@ -141,7 +137,7 @@ Sub IQQQDNlBYiabSN...()
141
137
EndSub
142
138
```
143
139
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.
145
141
146
142
### Step 2: Move .lnk to Startup Folder via Scheduled Task
147
143
@@ -169,12 +165,7 @@ Sub thCdGRXkYWmHai...()
169
165
EndSub
170
166
```
171
167
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.
178
169
179
170
### Step 3: Generate the C2 URL
180
171
@@ -188,7 +179,7 @@ The C2 server is at **`http://18.230.59.64/`** -> an AWS IP in the **São Paulo
188
179
189
180
### Step 4: Write Stage 2 VBS to %TEMP%
190
181
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:
192
183
193
184
```vbscript
194
185
Setfso=CreateObject("Scripting.FileSystemObject")
@@ -201,7 +192,7 @@ For i = 1 To 40 : writer.WriteLine randomVar & " = " & Chr(34) & randomString &
201
192
' ... continues for hundreds of lines
202
193
```
203
194
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.
205
196
206
197
### Step 5: Schedule Stage 2 Execution
207
198
@@ -223,7 +214,7 @@ Function mWWtQEHQcbKJBSu...(vbsPath)
223
214
EndFunction
224
215
```
225
216
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.
227
218
228
219
### Step 6: Wait for Signal, Then Execute Downloaded Payload
229
220
@@ -240,7 +231,7 @@ Loop
240
231
CallmWWtQEHQcbKJBSu...(downloadedPayloadPath)
241
232
```
242
233
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.
244
235
245
236
---
246
237
@@ -269,12 +260,7 @@ ie.Stop
269
260
ie.Quit
270
261
```
271
262
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.
278
264
279
265
I was able to access the C2 while it was still live and confirmed what the response looks like:
280
266
@@ -286,7 +272,7 @@ The C2 responds with a **second S3 URL** from a different bucket:
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 !).
290
276
291
277
### Chunked Binary Download via HTTP Range Requests
292
278
@@ -343,13 +329,9 @@ Function downloadPayload(url, savePath)
343
329
EndFunction
344
330
```
345
331
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.
351
333
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.
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.
|**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 |
| 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.
392
374
393
375
### Stage 3 C2 Protocol - Full Reversal
394
376
@@ -501,7 +483,7 @@ Size: 47 MB
501
483
502
484
### What It Actually Is
503
485
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.
505
487
506
488
```
507
489
CompanyName: Microsoft Corporation
@@ -514,7 +496,7 @@ InternalName: sfxcab
514
496
OriginalFilename: sfxcab.exe
515
497
```
516
498
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.
518
500
519
501
---
520
502
@@ -629,11 +611,11 @@ flowchart TD
629
611
630
612
## Conclusion
631
613
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.
633
615
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.
635
617
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.
637
619
638
620
If you see any S3 links with Portuguese filenames showing up in your inbox... maybe don't click them.
0 commit comments