c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36
Executive Summary
A 4.9 MB batch-script dropper that uses ~5,500 fake GOTO audit labels and 8,000 schtasks / reg query / wevtutil diagnostic lines as a static-analysis smokescreen. The actual payload is a PowerShell .NET assembly loader assembled from string-sliced variables and encoded with a single-character token-substitution cipher (#O→(, #R→$, #W→\n, etc.). The batch relaunches itself via conhost.exe --headless to suppress the console window, then extracts a Base64-encoded .NET assembly from a REM block and reflectively loads it. The sample is a structural sibling of the unclassified-batch-powershell-dropper family but represents a significantly more advanced obfuscation tier than prior siblings.
1. Build / RE
Toolchain
- Outer layer: Plain DOS batch file (
cmd.exe), no packer, no compiler. ^[file.txt] - Script size: 4,914,264 bytes; 98,346 lines. ^[file.txt]
- Inner layer: PowerShell 5.0+ with
System.Reflection.Assemblyreflective loading. - Target framework: .NET Framework (implied by
System.Net.ServicePointManagerandHttpClientreferences in decoded fragments).
Obfuscation — Three Nested Layers
Layer 1 — Caret escape + variable-slice alphabet
The batch uses caret-escaped commands (s^e^t, c^h^c^p, s^t^a^r^t) to defeat naive string grepping. A 69-character alphabet is built in variable _u via arithmetic expressions and concatenation:
_u = "tdr90pfIm/:a3VOFH75nEvzT168jXqolDSBhKuQL-bPNg4RZJM WseyckY.iwA\Cx2GU_"
Commands are then reconstructed by slicing single characters from _u using index variables computed via SET /A. For example:
_k("conhost") =_u[0]_u[55]_u[19]_u[35]_u[1]_u[31]_u[53]_bxf("headless") =_u[0]_u[52]_u[1]_u[37]_u[53]_u[55]_u[27]_sfe("/launched") = similarly sliced ^[analysis:Python decoder]
Layer 2 — Token-substitution PowerShell encoding
The PowerShell payload is split across multiple SET variables and uses a simple token cipher:
| Token | Replacement |
|---|---|
#O |
( |
#Q |
) |
#R |
$ |
#W |
newline |
#X |
[ |
#E |
] |
#D |
. |
#U |
- |
#I |
(deleted / no-op) |
Decoded fragments reveal:
$ErrorActionPreference = Stop$_iv = Invoke(method-name obfuscation for reflection)$_lm = & (ie-x)(($ExecutionContext.SessionState.LanguageMode))— language-mode gate (returns if not FullLanguage)$_tspm = [type]((Net.ServicePointManager))$_tspt = [type]((Net.SecurityProtocolType))$_tspm.SecurityProtocol = $_tspt.Tls12— forces TLS 1.2$_ra = [type]::GetType(((System.Reflection.Assembly)))Add-Type -AssemblyName (System.Net.Http)— loads HttpClient assembly ^[analysis:Python decoder]
Layer 3 — Smokescreen GOTO labels
5,586 labels of the form _WP<random> (e.g., _wp7nx3, _wpvvto, _wp9r1b), each followed by a block of fake Windows system-audit commands:
- Registry queries for PowerShell version (
reg query HKLM\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine) - Scheduled-task enumeration (
schtasks /query /tn "...") - Event-log inspection (
wevtutil qe ...) - Service status checks (
sc query WinDefend) - Firewall state queries (
netsh advfirewall show ...) - Disk-space checks (
dir /-C) - Timezone validation (
tzutil /g) - .NET Framework release checks
- Local group enumeration (
net localgroup)
These consume ~80% of the file by volume and have no execution path — every label is referenced only by the preceding GOTO and never reached by the outer control flow. ^[analysis:line count + structure]
Execution Flow
@echo offchcp 65001(UTF-8)- Build
_ualphabet viaSET /Aarithmetic - Slice
_uto reconstruct:_k="conhost",_bxf="headless",_sfe="/launched",_spz="Sysnative" - Relaunch:
start "" /b conhost.exe --headless cmd.exe /c "%~f0" /launched - On second pass (
/launchedflag detected), skip to the payload extraction block - Read own file via
[IO.File]::ReadAllText($env:_yj)where_yjis the batch filepath - Parse
REMlines, strip noise, decode Base64, load assembly via[Reflection.Assembly]::Load - Invoke entry point via reflection (
$_iv,GetMethod,Invoke)
Payload Carrier
The bulk of the file (after line ~700) consists of REM lines containing what appears to be Base64-like text with + and / characters. These are the .NET assembly payload fragments. The PowerShell code reassembles them by:
- Splitting the file on
[char]10 - Filtering lines starting with
REM - Stripping the
REMprefix and whitespace - Concatenating and decoding from Base64 ^[analysis:decoded _t fragment]
Notable Functions
- Language-mode gate:
if ($_lm -ne "FullLanguage") { return }— prevents constrained-language-mode environments from running the payload. - TLS pinning:
SecurityProtocol = Tls12— explicit downgrade/upgrade to TLS 1.2 (common in downloaders). - Reflective load: Uses
System.Reflection.AssemblywithGetMethod(..., BindingFlags)andInvoketo run the inner assembly without ever writing a PE to disk.
2. Deploy / ATT&CK
| Tactic | Technique | Evidence |
|---|---|---|
| Execution | T1059.003 (Windows Command Shell) | Batch file is the outer execution layer. ^[file.txt] |
| Execution | T1059.001 (PowerShell) | Inner payload assembled from SET variables and executed via cmd.exe /c. ^[analysis:decoded _xzg fragment] |
| Defense Evasion | T1027.010 (Obfuscated Files or Information) | 5,500+ fake GOTO labels, caret escaping, variable-slice alphabet, token-substitution cipher. ^[analysis:line count + structure] |
| Defense Evasion | T1620 (Reflective Code Loading) | [Reflection.Assembly]::Load + GetMethod/Invoke on inner .NET assembly. No file write. ^[analysis:decoded _xzg fragment] |
| Defense Evasion | T1207 (Rogue Domain Controller) | Not observed — no DC emulation. |
| Command and Control | T1071.001 (Application Layer Protocol: Web) | System.Net.Http.HttpClient referenced; TLS 1.2 forced. Likely second-stage download, but no static URL recovered. ^[analysis:decoded _xzg fragment] |
| Persistence | T1053.005 (Scheduled Task/Job) | schtasks referenced in smokescreen but not in active payload path — decoy only. |
| Discovery | T1082 (System Information Discovery) | Registry queries, service checks, event-log reads are all smokescreen decoys, not actual discovery. ^[analysis:line count + structure] |
C2 / Network
No static URL, IP, or domain was recovered from the decoded fragments. The PowerShell sets up HttpClient and TLS 1.2, suggesting the inner assembly performs its own C2 communication. The REM-block payload is the .NET assembly itself; no second-stage download URL is present in the outer script.
Attribution
- Family: unclassified-batch-powershell-dropper — same batch→PowerShell→.NET reflective chain, but this sample is architecturally distinct from prior siblings (cae0056ac, eda47a53) which used
SET/GOTOexpansion with%var%concatenation and paste-site downloaders. - Confidence: Medium. The TTP chain is consistent with the family (batch dropper, PowerShell reflective load, no disk write), but the obfuscation sophistication (token cipher, smokescreen labels, conhost headless relaunch) is a significant step above prior siblings and may indicate a new actor or builder version.
- No linguistic clues: All strings are English (Windows system strings); no Spanish/Portuguese indicators as seen in prior siblings.
3. Static-Only Caveats
CAPE did not detonate this sample (no Windows guest). All behavioural claims (reflective load, HttpClient usage, .NET assembly invocation) are inferred from decoded PowerShell fragments. The actual C2 protocol, exfil patterns, and inner-assembly functionality are unknown without dynamic execution.
Related
- unclassified-batch-powershell-dropper — Family entity page.
- batch-powershell-variable-expansion-obfuscation — Prior sibling obfuscation technique ( simpler
%var%concatenation). - batch-smokescreen-label-obfuscation — This sample's primary anti-static technique.
- powershell-token-substitution-loader — Token-cipher encoding used here.
- reflective-assembly-delegate-execution — Generic .NET reflective-loading pattern.