typeDOS batch file, ASCII textfamilyunclassified-batch-powershell-dropperconfidencemedium
SHA-256: c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36

c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36

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.Assembly reflective loading.
  • Target framework: .NET Framework (implied by System.Net.ServicePointManager and HttpClient references 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

  1. @echo off
  2. chcp 65001 (UTF-8)
  3. Build _u alphabet via SET /A arithmetic
  4. Slice _u to reconstruct: _k="conhost", _bxf="headless", _sfe="/launched", _spz="Sysnative"
  5. Relaunch: start "" /b conhost.exe --headless cmd.exe /c "%~f0" /launched
  6. On second pass (/launched flag detected), skip to the payload extraction block
  7. Read own file via [IO.File]::ReadAllText($env:_yj) where _yj is the batch filepath
  8. Parse REM lines, strip noise, decode Base64, load assembly via [Reflection.Assembly]::Load
  9. 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 REM prefix 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.Assembly with GetMethod(..., BindingFlags) and Invoke to 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/GOTO expansion 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