typetechniqueconfidencehighcreated2026-06-03updated2026-07-03obfuscationscriptdefense-evasionanti-debug

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) and GOTO jumps 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 sequence f@ 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

  1. Write a PowerShell one-liner, e.g.:
    $a = [System.Text.Encoding]::UTF8.GetBytes('Hello'); [Convert]::ToBase64String($a)
    
  2. Encode it as UTF-16LE raw bytes (not base64) and split into 60-character chunks.
  3. 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%..."
    
  4. 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.exe command 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.exe spawning powershell.exe where the command-line length exceeds 4000 characters and the parent is a .bat file.

Pages Where Observed

  • unclassified-batch-powershell-dropper — entity page for this family

  • unclassified-batch-powershell-dropper report ^[/intel/analyses/cae0056acc2f1b6285544c96e33a4e4c49b964f309b8e4df08b9bf55695389b8.html]

  • unclassified-js-webdav-dropper — same SET variable fragmentation pattern used for net use + regsvr32 WebDAV chain rather than PowerShell cradle

  • ffd5d894 — pure batch variant using natural-language phrase variable names (e.g. ripeguaranteejar, sipdestructionlabored) decoded directly via batch %VAR% expansion, no JScript polyglot layer ^[/intel/analyses/ffd5d894f5e350baace2342fc3c2c7de63a82138469db1694c35c732cf0c9df4.html]

  • e777fd64 — pure batch variant using shared-prefix short-suffix SET pattern (hbutcXXXX), PowerShell hidden wrapper, rundll32 WebDAV execution. Same fragmentation technique, different payload delivery chain. ^[/intel/analyses/e777fd64b88a066748001a99e30ed1c4b46d9f8c4cbeee322a78d1fc06e5062f.html]

  • 976dc607 — pure batch variant with leading word-salad padding (~40 lines of natural-language noise before the :signal label), natural-language SET obfuscation (squealpremiumcollect, deadperfectabrasive), WebDAV C2 45.9.74.36:8888, regsvr32 /s execution. Same fragmentation technique; padding is a new anti-triage refinement. ^[/intel/analyses/976dc6079cdd2f55efaccf08f0b16317523e6143dddbe825a82aa23be9d74acc.html]

  • 682bea06 — pure batch, natural-language SET obfuscation (36 variables, e.g. northfemaledifferent, icicledepressedmatch), WebDAV C2 45.9.74.36:8888, 28078514069.dll payload, regsvr32 /s execution, PowerShell -windowstyle hidden wrapper, color f0 \u0026\u0026 start wordpad decoy, 48 lines leading word-salad padding before :wind label. Thirty-seventh confirmed sibling. Near-identical execution engine to ffd5d894 cluster; only SET phrases and payload filename differ. Static-only (batch, not a binary). Report written. ^[/intel/analyses/682bea06c5cfeabc74f1a08f5a0383bb48c6787b9c08d4371efa2c3c7753bd55.html]

  • 4889c168 — pure batch, shared-prefix SET obfuscation (epykh, 36 variables), WebDAV C2 45.9.74.32:8888, 4551.dll payload, rundll32 ...,entry execution, color f0 \u0026\u0026 start wordpad decoy, PowerShell -windowstyle hidden wrapper, three-line minimal footprint. Sixty-third confirmed sibling. Smallest pure-batch variant in the family; identical SET expansion technique. Static-only (batch, not a binary). Report written. ^[/intel/analyses/4889c1683ff9be39c6fdfc9f1def785309c7a9ab74d89a3bf4f6e4bca4990caa.html]

  • unclassified-batch-string-slice-dropper — distinct variant using a single shared character array (%Z:~n,1%) instead of multiple SET variables.

References

  • cae0056ac batch reconstruction: ^[/intel/analyses/cae0056acc2f1b6285544c96e33a4e4c49b964f309b8e4df08b9bf55695389b8.html]