typeanalysisfamilyunclassified-python-ngrok-ratconfidencelowcreated2026-06-14scriptdropperratc2persistencediscoverycredential-accessdefense-evasionimpact
SHA-256: 028301d695ca8918d856cacd951ae7fa54c6a9ab77edf975e1259fa243cc5b71

unclassified-python-ngrok-rat: 028301d6 — Batch stager fetching Python embed, dropping zlib+XOR-obfuscated Python RAT

Executive Summary

A Windows batch script masquerading as a "System Update Service Initializer" downloads the Python 3.10.11 embeddable runtime from python.org via curl, extracts it to %TEMP%\win_kb_update, decodes an embedded base64+zlib+XOR-obfuscated Python script, and launches it as a daemon thread. The inner payload is a TCP socket RAT beaconing to 0.tcp.in.ngrok.io:13811 with ~15 commands including screenshot, WiFi credential theft, Chrome DPAPI master-key extraction, cookie/password exfiltration, remote shell, schtasks persistence, and BSOD trigger. 18 KB total — low-skill, high-capability commodity malware likely built from open-source RAT tutorials.

What It Is

  • File: Update.bat ^[file.txt] ^[exiftool.json]
  • Size: 18,385 bytes
  • Type: DOS batch file, ASCII text (Unix LF newlines) ^[file.txt]
  • Family: Unattributed — no matching cluster in corpus. Low-confidence label: unclassified-python-ngrok-rat.
  • Build: Hand-written batch + hand-written Python. No packer, no compiler, no signing. Obfuscation is manual base64 line-splitting + zlib compression + single-byte XOR with a 16-character key (gKw6tnluBXn4m7Gt). ^[strings.txt:167–186] ^[decoded_payload.py:1]

How It Works

Stage 1 — Batch Stager

The batch script (@echo off) sets three variables: Z = %TEMP%, K = %Z%\win_kb_update, L = %Z%\ldr_663.py. ^[strings.txt:2–5]

If %K%\python.exe does not exist, it:

  1. Creates %K%
  2. Downloads https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-win32.zip via curl -L -s -k -o ^[strings.txt:9]
  3. Extracts with tar -xf (Windows 11 / modern tar)
  4. Deletes the zip
  5. Clears temp debris (b64.tmp, ldr_663.py) ^[strings.txt:16–17]

Then it writes 169 echo lines appending base64 chunks to %TEMP%\b64.tmp, decodes with certutil -decode to %L%, and spawns %K%\python.exe "%L%" in background (start "" /B). ^[strings.txt:187–193]

Stage 2 — Outer Python Loader

The decoded %L% file is a ~10 KB Python loader (SystemConfigurationLoader) that:

  1. Concatenates three base64 strings (ASSET_CHUNKS) ^[decoded_payload.py:5–9]
  2. Base64-decodes the concatenation
  3. zlib-decompresses the result
  4. XOR-decrypts with CONFIG_HASH = "gKw6tnluBXn4m7Gt" (same key as the batch-level obfuscation)
  5. exec()s the resulting script in globals() ^[decoded_payload.py:27–31]

The loader runs in a daemon thread with a heartbeat loop that restarts the worker if it dies. ^[decoded_payload.py:40–48]

Stage 3 — Inner Python RAT

The final payload is a ~12.7 KB Python RAT with a hardcoded C2: ^[decoded_payload.py:6–8]

HOST = '0.tcp.in.ngrok.io'
PORT = 13811
key = "14"

On connect it sends [CONNECTED] 14 plus an [INTEL] block with platform, hostname, OS release, version, machine, processor, logged-in user, and public IP from ip-api.com. ^[decoded_payload.py:27–35]

Supported commands (verbatim from payload):

  • screenshot — PowerShell SendKeys PrtSc + Clipboard JPEG → base64 via subprocess.check_output ^[decoded_payload.py:41–44]
  • wifinetsh wlan show profiles + key=clear for each SSID ^[decoded_payload.py:46–56]
  • intel — Re-sends system enumeration block ^[decoded_payload.py:58–65]
  • persistschtasks /create /tn "SystemUpdate" /tr <script_path> /sc onlogon /rl highest /f ^[decoded_payload.py:67–71]
  • bsodRtlAdjustPrivilege(19, 1, 0, ...) + NtRaiseHardError(0xC000007B, ...) ^[decoded_payload.py:73–77]
  • whatsapp — Zips Chrome Local Storage leveldb directory and sends base64 ^[decoded_payload.py:79–103]
  • webcam — Calls avicap32.capCreateCaptureWindowA; no actual frame capture logic (stub) ^[decoded_payload.py:105–114]
  • keylog_start — No-op stub ^[decoded_payload.py:116–119]
  • master_key — Chrome DPAPI decryption of Local State os_crypt.encrypted_key via CryptUnprotectData ^[decoded_payload.py:120–143]
  • passwords — Copies Login Data SQLite to TEMP and sends raw bytes ^[decoded_payload.py:145–155]
  • cookies — Copies Network\Cookies SQLite to TEMP and sends raw bytes ^[decoded_payload.py:157–167]
  • cmd <string>subprocess.Popen(cmd_str, shell=True, ...) with cmd.exe fallback ^[decoded_payload.py:169–190]
  • ls [path] / cd [path] / pwd / dir — Directory/file enumeration ^[decoded_payload.py:192–220]

The payload imports cv2 and numpy opportunistically but never uses them (likely copy-paste from a tutorial). ^[decoded_payload.py:11–15]

C2 Infrastructure

Indicator Value Notes
C2 host 0.tcp.in.ngrok.io ngrok free-tier TCP tunnel; ephemeral — useful for tracking actor registration habits but not long-lived IOC
C2 port 13811 Hardcoded in inner payload ^[decoded_payload.py:7]
Session key 14 Hardcoded beacon prefix ^[decoded_payload.py:8]
IP lookup http://ip-api.com/line/?fields=query Public IP for victim identification ^[decoded_payload.py:32]
Python source https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-win32.zip Legitimate Python.org download; abuse of open-source infrastructure ^[strings.txt:9]

Interesting Tidbits

  • No anti-analysis whatsoever. No VM checks, no debug checks, no timing gates, no process enumeration. Relies entirely on being a batch file that sandboxes may skip. ^[strings.txt:1–193]
  • Duplicate key reuse. The same 16-byte string gKw6tnluBXn4m7Gt serves as both the batch-level CONFIG_HASH and the inner payload XOR key. Amateur opsec. ^[strings.txt:167] ^[decoded_payload.py:11]
  • Error-mode suppression. _init_env() calls ctypes.windll.kernel32.SetErrorMode(0x8003) (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX), hiding crashes from the user. ^[decoded_payload.py:17–19]
  • Comment masquerade. Outer Python loader claims to be "Asset Manager for Internal Physics System" — meaningless dead text to confuse static readers. ^[decoded_payload.py:4]
  • cv2/numpy dead imports. The import cv2 and import numpy lines are present but never referenced. Likely residue from a tutorial or builder template that included webcam streaming. ^[decoded_payload.py:11–15]
  • AVICAP32 stub. The webcam command creates a capture window but has no frame-grab or transmission logic. The comment admits: "Full stream logic requires complex struct marshaling, simplified response for FUD." ^[decoded_payload.py:105–114]
  • Batch line count. 169 echo lines at ~80 chars each = 13,504 bytes of base64, encoding ~10,128 bytes of compressed payload. The batch is 18,385 bytes total; the payload is ~55% of the file.
  • Windows version compatibility. Uses tar -xf (available in Windows 10 17063+ and Windows 11) rather than expand or powershell Expand-Archive, suggesting author targets modern Windows but doesn't know PowerShell.

How To Mess With It (Homelab Replication)

Toolchain

  • Any text editor + Windows 10/11 host with curl.exe and tar.exe
  • Python 3.10+ (target host)
  • Optional: ngrok account for C2 tunnel

Working reproduction

  1. Write a batch file that downloads python-3.10.11-embed-win32.zip, extracts to %TEMP%\win_kb_update.
  2. Embed a Python script as base64 lines (one per echo), decode with certutil.
  3. The Python script should be a zlib-compressed + XOR-encrypted blob wrapped in a loader.
  4. Inner payload: TCP socket loop to 0.tcp.in.ngrok.io:<port>, s.send("[CONNECTED] <key>"), then a dispatch table.

Verification

Run the batch on a Windows VM. Monitor with ProcMon for curl.exepython.exe child process. Network capture should show TCP SYN to 0.tcp.in.ngrok.io:13811. No capa verification possible — capa rejected the input as unsupported. ^[capa.txt]

What you'll learn

This demonstrates how a trivial batch script + legitimate Python runtime can bypass AV that focuses on PE signatures. The lesson is behavioral: watch for curl.exepython.exe chains from %TEMP% and TCP sockets spawned by python.exe in user-writable directories.

Deployable Signatures

YARA Rule

rule Python_Ngrok_RAT_Batch_Stager
{
    meta:
        description = "Batch script stager downloading Python embed and dropping ngrok-beaconing RAT"
        author = "PacketPursuit"
        date = "2026-06-14"
        hash = "028301d695ca8918d856cacd951ae7fa54c6a9ab77edf975e1259fa243cc5b71"
    strings:
        $batch1 = "@echo off" ascii
        $batch2 = "curl -L -s -k -o" ascii
        $batch3 = "python-3.10.11-embed-win32.zip" ascii
        $batch4 = "certutil -decode" ascii
        $py1 = "SystemConfigurationLoader" ascii
        $py2 = "load_update_package" ascii
        $py3 = "ASSET_CHUNKS" ascii
        $py4 = "0.tcp.in.ngrok.io" ascii
        $py5 = "[CONNECTED]" ascii
        $py6 = "ip-api.com/line/?fields=query" ascii
        $py7 = "RtlAdjustPrivilege" ascii
        $py8 = "NtRaiseHardError" ascii
        $xor_key = "gKw6tnluBXn4m7Gt" ascii
    condition:
        (uint16(0) == 0x6540 or uint16(0) == 0x4065 or uint16(0) == 0x0a40) // @e or @n or \n@
        and 2 of ($batch*)
        and 2 of ($py*)
        and filesize < 30KB
}

Behavioral Hunt Query (Sigma-like)

title: Python Embed Downloaded by Batch Followed by TCP Socket
detection:
  selection_curl:
    - Image|endswith: 'curl.exe'
    - CommandLine|contains: 'python-3.10.11-embed-win32.zip'
  selection_python:
    - Image|endswith: 'python.exe'
    - ParentImage|endswith: 'cmd.exe'
  selection_network:
    - Initiated: true
    - Image|endswith: 'python.exe'
    - DestinationPort: 13811
  condition: selection_curl or (selection_python and selection_network)

IOC List

Type Value
SHA-256 028301d695ca8918d856cacd951ae7fa54c6a9ab77edf975e1259fa243cc5b71
Filename Update.bat
Staging dir %TEMP%\win_kb_update
Dropped script %TEMP%\ldr_663.py
Temp file %TEMP%\b64.tmp
C2 host 0.tcp.in.ngrok.io
C2 port 13811
Session key 14
XOR key gKw6tnluBXn4m7Gt
SchTasks name SystemUpdate
Python URL https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-win32.zip
IP lookup http://ip-api.com/line/?fields=query

Behavioral Fingerprint

On execution, this artifact spawns curl.exe to download the Python 3.10.11 embeddable zip from python.org to %TEMP%, extracts it with tar.exe into %TEMP%\win_kb_update, then uses certutil.exe to base64-decode a 169-line payload from a batch file into %TEMP%\ldr_663.py. The resulting python.exe process immediately opens a TCP socket to 0.tcp.in.ngrok.io:13811, sends a [CONNECTED] 14 beacon, followed by an [INTEL] block containing system enumeration data fetched from ip-api.com. Network traffic is cleartext TCP with no TLS. Detection opportunity: python.exe spawned from cmd.exe with a command line referencing %TEMP%\ldr_663.py and an immediate outbound connection to an ngrok subdomain on a high ephemeral port.

Detection Signatures

  • MITRE ATT&CK mapping (inferred from static payload; no CAPE detonation):
    • T1059.003 — Windows Command Shell (batch execution)
    • T1059.006 — Python (inner payload)
    • T1071.001 — Application Layer Protocol: Web Protocols (ip-api.com HTTP lookup)
    • T1041 — Exfiltration Over C2 Channel (TCP socket exfil)
    • T1048.003 — Exfiltration Over Alternative Protocol (TCP socket, non-standard port)
    • T1083 — File and Directory Discovery (ls, dir, cd)
    • T1082 — System Information Discovery (intel command — platform, hostname, processor)
    • T1049 — System Network Connections Discovery (wifi command — netsh profiles)
    • T1005 — Data from Local System (Login Data, Cookies, WhatsApp local storage)
    • T1555.003 — Credentials from Web Browsers (Chrome DPAPI master key, Login Data SQLite)
    • T1552.004 — Private Keys (DPAPI CryptUnprotectData on os_crypt.encrypted_key)
    • T1056.001 — Input Capture: Keylogging (stub — keylog_start is no-op)
    • T1113 — Screen Capture (PowerShell SendKeys + Clipboard JPEG)
    • T1125 — Video Capture (AVICAP32 stub)
    • T1547.001 — Registry Run Keys / Startup Folder (schtasks onlogon persistence)
    • T1053.005 — Scheduled Task/Job: Scheduled Task (same schtasks mechanism)
    • T1490 — Inhibit System Recovery (BSOD via NtRaiseHardError)
    • T1567.002 — Exfiltration Over Web Service (WhatsApp session zip via socket)

No dynamic execution was performed (CAPE skipped: not a supported binary class). ^[dynamic-analysis.md] All TTPs are inferred from static string and decompiled payload analysis.

References

  • SHA-256: 028301d695ca8918d856cacd951ae7fa54c6a9ab77edf975e1259fa243cc5b71
  • Original filename: Update.bat ^[metadata.json]
  • Artifact ID: 479360c9-9766-4895-850a-e46fc6f2bc95 ^[metadata.json]
  • Source: OpenCTI / MalwareBazaar ^[metadata.json]
  • Related entity: unclassified-python-ngrok-rat
  • Related concept: python-packed-payload
  • Related procedure: schtasks-onlogon-persistence

Provenance

Analysis built from:

  • file.txt, exiftool.json, metadata.json, triage.json — file metadata
  • strings.txt — full batch script content (line-level citations)
  • decoded_payload.py — manually decoded outer Python loader + inner RAT payload (decoded from base64+zlib+XOR chain embedded in strings.txt)
  • dynamic-analysis.md — CAPE skipped status
  • capa.txt — capa rejected unsupported file format

decoded_payload.py was produced by reversing the base64→zlib→XOR chain described in the batch script's SystemConfigurationLoader.load_update_package() method. The decoding steps exactly mirror the runtime logic: concatenate ASSET_CHUNKS strings, base64-decode, zlib-decompress, then byte-wise XOR with key gKw6tnluBXn4m7Gt.