Batch PowerShell Variable Expansion Obfuscation
What It Does
A DOS batch-script anti-static technique in which a PowerShell payload is fragmented across tens of SET variable assignments, then reassembled in a single line via %VARNAME% expansion and passed as an -ArgumentList to a nested powershell.exe -WindowStyle Hidden invocation. The goal is to defeat naive string extraction and signature matching, because no single line contains a complete keyword like powershell or FromBase64String.
Detection / Fingerprint
Look for these indicators in a .bat or .cmd file:
-
30
SET VARNAME=lines with >60 character values. - A single line containing 50+
%VARNAME%concatenations (no spaces between expansions). - That same line contains
powershell.exe,Start-Process, or-WindowStyle Hidden. - Labels (
:LABELNAME) andGOTOjumps interleaved between SETs, forming a chain. - The file is ASCII text, 5–15 KB, with very long lines (500–800 chars) but no complex control flow (loops, IF blocks).
Implementation Patterns Observed
In sample cae0056ac (8.4 KB batch), the script uses 63 variables, each holding a slice of raw UTF-16LE characters, assembled on one line into a full powershell.exe -Command "Start-Process … -ArgumentList …" call. ^[/intel/analyses/cae0056acc2f1b6285544c96e33a4e4c49b964f309b8e4df08b9bf55695389b8.html]
Obfuscation refinements observed:
f@insertion — The assembled string embeds the sequencef@inside valid base64; PowerShell regex-replaces it before decoding. ^[decoded_payload.txt]- Method-name splitting —
'run' + 'ss'instead of'runss', evading exact-string YARA. ^[decoded_payload.txt] - Tag splitting —
<<BASE64_START>>built from'base64'concatenated with literal prefix and suffix. ^[decoded_payload.txt] - URL reversal — The final C2 URL is stored reversed (
txt.sabeurp/niam/war/-/...) and cleaned with a trailing substring strip before use. ^[decoded_payload.txt]
Reproduce on Your Own VMs
- Write a PowerShell one-liner, e.g.:
$a = [System.Text.Encoding]::UTF8.GetBytes('Hello'); [Convert]::ToBase64String($a) - Encode it as UTF-16LE raw bytes (not base64) and split into 60-character chunks.
- Create a
.bat:@echo off GOTO START :A1 SET V01=ABC... GOTO A2 :A2 SET V02=DEF... ... :START %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -Command "%V01%%V02%..." - Test against your EDR / sandbox — does it surface the full PowerShell string in process arguments? A basic cmd.exe logger usually captures the final expanded line, but static YARA against the .bat file will be blind to the payload unless it looks for concatenation patterns.
Defensive Countermeasures
- EDR: Log parent
cmd.execommand lines with >50%VAR%tokens; these are abnormal for legitimate batch files. - YARA: See the rule in unclassified-batch-powershell-dropper analysis.
- Behavioral: flag any
cmd.exespawningpowershell.exewhere the command-line length exceeds 4000 characters and the parent is a.batfile.
Pages Where Observed
- unclassified-batch-powershell-dropper — entity page for this family
- unclassified-batch-powershell-dropper report ^[/intel/analyses/cae0056acc2f1b6285544c96e33a4e4c49b964f309b8e4df08b9bf55695389b8.html]
References
cae0056acbatch reconstruction: ^[/intel/analyses/cae0056acc2f1b6285544c96e33a4e4c49b964f309b8e4df08b9bf55695389b8.html]