typeanalysisfamilyunclassified-batch-string-slice-dropperconfidencemediumcreated2026-06-17updated2026-06-17scriptdropperexecutiondefense-evasionobfuscation
SHA-256: aa443a52851efff9b5c218c82c0ff54e9b87d6100ae80e915f3a8cca41d107b6

unclassified-batch-string-slice-dropper: aa443a5285 — FTSP.zip Python stager with 64-char string-slice obfuscation

Executive Summary

A 7.9 KB Windows batch script that uses character-array substring slicing (%Z:~n,1%) against a 64-character master string to assemble its plaintext commands. The script stages a local Python 3.12 runtime under %USERPROFILE%\Downloads\Print\Python312\ and executes seven sequentially-numbered .py payloads, then cleans up the staging directory and hides the Print folder. No network C2, no persistence, no anti-analysis — the entire threat is the obfuscated batch itself and whatever payloads it expects to find already staged as FTSP.zip.

What It Is

  • File: random.bat ^[metadata.json]
  • Type: UTF-8 text with BOM, DOS batch script ^[file.txt]
  • Size: 7 891 bytes ^[triage.json]
  • SHA-256: aa443a52851efff9b5c218c82c0ff54e9b87d6100ae80e915f3a8cca41d107b6 ^[triage.json]
  • Family: unclassified-batch-string-slice-dropper (new label; n=1 in corpus, sibling eae731d3 shares same obfuscation engine but different payload)
  • Build: Hand-written batch; no packer, no compiler, no signing.

How It Works

Obfuscation Engine

The script stores a single 64-character master string in variable Z:

QlG4MmXfVkw@8oWg FZP3ORHezDiLBupcn7r1tvIjYqhJ6ba5KNC2xAT9y0sESdU

Every plaintext character in the payload is emitted by expanding %Z:~n,1% where n is the zero-based index into the master string. The expansions are concatenated on single lines with %% batch escaping, producing the final commands. ^[strings.txt:1-500]

This is a distinct variant from the SET/GOTO variable-expansion family observed in cae0056ac and eda47a53 (batch-powershell-variable-expansion-obfuscation). There, the payload is fragmented across dozens of variables; here, a single shared character array is indexed. The result is the same — static string extraction sees only the master string and a wall of ~n,1 references, not the assembled commands.

Decoded Behavior

After de-obfuscation, the script performs the following:

  1. Sets destination=%USERPROFILE%\Downloads\FTSP.zip.
  2. Changes directory to %Userprofile%\Downloads\Print\Python312.
  3. Executes python.exe 1.py through 7.py sequentially.
  4. Deletes %USERPROFILE%\Downloads\new.bat if it exists.
  5. Deletes %destination% (FTSP.zip) if it exists.
  6. Sets attrib +h on %USERPROFILE%\Downloads\Print to hide the staging folder.

^[decoded.txt]

Missing Pieces (Static Inference)

  • The script references FTSP.zip as a destination but never downloads or extracts it in the recovered text. Either the .zip is pre-staged by an earlier stage, or the download command was not recovered due to an index-offset mismatch in the de-obfuscation.
  • No curl, bitsadmin, certutil, or powershell download cradle is visible in the decoded output. The seven Python scripts (1.py7.py) are the actual payload; their content is not present in this artifact.
  • No persistence mechanism is present in the batch itself. Persistence, if any, would be implemented inside the Python layer or by a separate dropper.

Decompiled Behavior

N/A — this is a plaintext batch script, not a compiled binary. No Ghidra or radare2 analysis is applicable. The entire logic is recovered via manual substitution of %Z:~n,1% references against the master string.

C2 Infrastructure

No network indicators recovered from static analysis. The script is strictly local execution — it assumes Python312 and the .py payloads are already present on disk. Attribution to a specific delivery mechanism (USB, previous-stage downloader, insider) is not possible from this artifact alone.

Interesting Tidbits

  • Low-skill, high-noise obfuscation: The master string contains a literal space (…@8oWg FZP3…), which is index 16 and used as the space character in assembled strings. This confirms the author was manually tuning the character set rather than using an automated encoder. ^[strings.txt:2]
  • No error handling: The script does not check whether Python312 exists before cd into it. On a clean host, the python.exe calls would fail silently and the cleanup would still run.
  • Self-cleanup with folder-hide: The combination of deleting new.bat, deleting FTSP.zip, and hiding the Print folder is classic anti-forensics — remove the staging artifacts and make the parent directory hidden. ^[decoded.txt]
  • Sibling evidence: Sample eae731d3 (VT_startupppp.bat) uses the same %Z:~n,1% pattern against a different master string, confirming this obfuscation engine is reused across campaigns. ^[sample eae731d3/strings.txt]

How To Mess With It (Homelab Replication)

This obfuscation is trivial to replicate and trivial to defeat.

Replication:

  1. Write a batch script that sets Z=<64-character alphabet>.
  2. Encode every character of your payload as %Z:~n,1% where n is the index.
  3. Concatenate on a single line with %% escaping.
  4. The result is a .bat file that looks like a wall of %Z:~n,1%%Z:~m,1%… but executes normally.

Defeat:

  1. Extract the master string from the set "Z=…" line.
  2. Replace every %Z:~n,1% with Z[n].
  3. Strip remaining %VAR% tokens (most are decoy noise).
  4. The plaintext payload is recovered.

Deployable Signatures

YARA Rule

rule Batch_StringSlice_Obfuscation
{
    meta:
        description = "Batch script using %Z:~n,1% character-array slicing"
        author      = "pp-hermes"
        date        = "2026-06-17"
    strings:
        $setz  = /set\s+"[^=]*Z=[A-Za-z0-9@ ]{40,70}"/
        $slice = /%~?[A-Za-z0-9]*:~\d+,1%/
    condition:
        uint16(0) != 0x5a4d and  // not a PE
        $setz and
        #slice > 50
}

Sigma Rule

title: Batch Script String-Slice Obfuscation Execution
status: experimental
description: Detects cmd.exe spawning python.exe after executing a batch file containing %Z:~n,1% character-slice patterns.
logsource:
    category: process_creation
    product: windows
detection:
    selection_parent:
        ParentImage|endswith: '\cmd.exe'
        CommandLine|contains: 'python.exe'
    selection_child:
        CommandLine|contains:
            - '~0,1%'
            - '~1,1%'
            - '~2,1%'
            - '~3,1%'
    condition: selection_parent and selection_child
falsepositives:
    - Legitimate batch files using substring extraction (rare in bulk)
level: medium

Behavioral Hunt Query (KQL)

DeviceProcessEvents
| where InitiatingProcessFileName =~ "cmd.exe"
| where FileName =~ "python.exe"
| where InitiatingProcessCommandLine contains "~0,1%" or InitiatingProcessCommandLine contains "~1,1%"
| summarize count() by DeviceName, InitiatingProcessCommandLine

IOC List

Indicator Value Type
SHA-256 aa443a52851efff9b5c218c82c0ff54e9b87d6100ae80e915f3a8cca41d107b6 File
Filename random.bat Filename
Staging directory %USERPROFILE%\Downloads\Print\Python312 Directory
Staging archive %USERPROFILE%\Downloads\FTSP.zip File
Self-cleanup target %USERPROFILE%\Downloads\new.bat File
Hidden folder %USERPROFILE%\Downloads\Print Directory

Behavioral Fingerprint

This artifact is a batch-script stager that relies on character-array substring slicing (%Z:~n,1%) to obfuscate its commands. It expects a pre-staged Python 3.12 runtime and seven sequentially-numbered .py scripts under %USERPROFILE%\Downloads\Print\Python312\. After execution, it deletes new.bat and FTSP.zip, then sets the Print folder to hidden. No network C2, no persistence, and no anti-analysis — the entire threat is local staging and cleanup.

Detection Signatures

N/A — no capa, no floss, no YARA matches, and CAPE skipped because the file is plaintext. Detection relies on behavioral rules (process tree + command-line patterns) and static YARA against the obfuscation engine.

References

  • Sibling analysis (same obfuscation engine): eae731d3ad19c8258854dd7df817003a24361c44fb43ca91e2ed076182beab64 (VT_startupppp.bat)
  • Related technique: batch-powershell-variable-expansion-obfuscation — different mechanism (SET/GOTO fragmentation vs single character array)

Provenance

  • File type: file.txt (file command)
  • Strings: strings.txt (raw UTF-8 with BOM)
  • Decoded payload: decoded.txt (manual substitution of %Z:~n,1% against master string)
  • Triage metadata: triage.json
  • Master string length: 64 characters; alphabet: QlG4MmXfVkw@8oWg FZP3ORHezDiLBupcn7r1tvIjYqhJ6ba5KNC2xAT9y0sESdU