2026-04-09
3638 words
18 minutes
Slithering Through the Noise - Deep Dive into the VIPERTUNNEL Python Backdoor

Introduction#

During a recent DragonForce ransomware engagement, we identified several artefacts indicative of anomalous behaviour inconsistent with typical campaign activity. During a persistence review, we aggregated Autoruns output from all endpoints to identify anomalies. A scheduled task named 523135538 was identified, configured to execute C:\ProgramData\cp49s\pythonw.exe without command-line arguments. Executing pythonw.exe without a script path or -c argument is atypical in legitimate Windows environments. Although DLL sideloading was initially suspected, analysis of the containing directory indicated a different persistence mechanism. Within the directory, we found C:\ProgramData\cp49s\Lib\sitecustomize.py. In Python, this module auto-imports at startup. Placing malicious code here ensures it runs whenever pythonw.exe starts, without command-line input. Analysis of sitecustomize.py clarified why the absence of command-line arguments was sufficient. The script leverages ctypes to invoke Python C API functions and infer its runtime execution context:

import runpy, os, sys, ctypes
a = ctypes.c_int()
p = ctypes.POINTER(ctypes.c_wchat_p)()
ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(a), ctypes.byref(p))
if a.value == 1:
    d = os.path.dirname(sys.executable)
    t = os.path.join(d, "b5yogiiy3c.dll")
    runpy.run_path(t, run_name='__main__')

Using Py_GetArgcArgv, the script checks argc. An argc of 1 (just pythonw.exe) means no command-line input. Under this, it locates b5yogiiy3c.dll in C:\ProgramData\cp49s and runs it via runpy.

Analysis of b5yogiiy3c.dll#

The file b5yogiiy3c.dll is an python script masquerading as a dynamic link library, with several layers of obfuscation.

The sections below detail the techniques used and the steps to recover the original Python source code.

Layers of obfuscation#

The contents of b5yogiiy3c.dll was heavily obfuscated with layered abstraction, obscuring its behaviour. Analysis required manually reversing these layers. The sections below detail the techniques used and the steps to recover the original Python source code. The import statements hint at the script’s purpose, enabling capability assessment before analysing obfuscated logic.

Some modules outside the Python Standard Library are embedded in C:\ProgramData\cp49s\ to ensure reliable execution without requiring pip. The cryptographic primitives—hash functions (blake3, SHA256, hashlib), symmetric ciphers (AES, ChaCha20), encoding tools (base64, zlib), and sys—highlight the script’s purpose, with unused os likely due to code reuse. This setup suggests a loader decrypts and stages a secondary payload. The script then masks function names and core Python calls with random variables to thwart static analysis.

Both screenshots show imported methods assigned to variables, with zlib.decompress prepared for later to handle compressed payloads. It also uses base64.b85decode, indicating Base85 encoding, which has a higher density and can bypass systems focused on standard Base64 patterns. Further analysis identifies two key decoding functions forming the core translation layer. Since the script contains little cleartext, these routines are the only mechanism to convert obfuscated blobs into executable logic. One reverses Base85 encoding, and the other converts integers to strings.

Below are deobfuscated decoding functions, preserving their original logic.

Tracing the script’s control flow leads to the final obfuscated payload at the end of the file.

Below are the deobfuscated decryption functions, preserving their original logic.

A large, high-entropy blob serves as the encoded payload. This blob is fed to WgGsgQuaeeYg7e(), which decodes and decrypts it using helper functions. The payload is processed with compile(), using a synthetic filename (<jK6xvQeYbpkDD>) and exec mode, then executed immediately. This keeps the next-stage logic in memory, reducing detection risk.

The decryption function employs control-flow flattening, which makes manual analysis more difficult. It runs in a while True loop, with a state variable controlling flow, updated each step instead of following a linear sequence.

Each subsequent stage follows the same pattern, just with different payloads, totalling three obfuscation layers before reaching the final payload. A public unpacker for this obfuscation framework is available from eSentire. However, in this case, the implementation differs slightly, and the unpacking strategy used by that script no longer works.

The Final Stage#

After removing three obfuscation layers, the payload was recovered. The script creates a SOCKS5 proxy with an outbound tunnel to a hardcoded C2 server. While default C2 parameters and credentials are embedded, the proxy can also accept alternate C2 addresses via command-line arguments. Due to the incident’s age, no other C2 endpoints were observed in our telemetry. The proxy uses port 443 for outbound connections, blending with typical HTTPS traffic to evade detection.

The payload includes three classes: Wire, Relay, and Commander. The Commander class inherits from threading.thread, serves as the main control thread, performs the initial C2 handshake, and spawns Relay instances as needed. The Relay class implements SOCKS5 proxy logic, mediating data between the C2 endpoint and local network once instantiated by Commander. The Wire class supports this by managing socket abstractions and encapsulating tunnel data. The modular design supports concurrent proxy sessions, enabling scalable traffic relay.

Further Hunting#

Attribution#

We assess that the Python backdoor is linked to UNC2165 and EvilCorp, according to Google Threat Horizons and GuidePoint Security research. Its SOCKS5 tunnelling and modular design match documented VIPERTUNNEL traits. Often used as a follow-on payload from FAKEUPDATES infections, it serves for persistence and network pivot. Such access is usually monetized or sold to ransomware groups like RansomHub. In this case, ransomware activity was linked to DragonForce, but there’s insufficient evidence to confirm VIPERTUNNEL’s access was transferred or sold to them.

Same Obfuscation, different samples#

Threat hunting uncovered multiple samples using the same obfuscation framework to deliver ShadowCoil (also known as COILCAGE or RATTLEGRAB by Google), a Python credential-stealing malware. eSentire reports that ShadowCoil targets Chromium-based browsers such as Chrome and Edge; however, it has also been found to extract credentials from Firefox, indicating ongoing development to expand its reach. The overlap suggests ViperTunnel and ShadowCoil are from the same former RansomHub affiliates. They share a multi-stage packer—likely a private utility. This obfuscation is tied to the activity cluster, making its logic a strong indicator of their operation, whether it’s a proxy or credential stealer. One notable difference in the obfuscation is that the ShadowCoil samples include additional anti-debugging checks, as illustrated in the following screenshot.

The check for TracerPid in /proc/self/status detects if a process is traced, a Linux-specific anti-debugging technique. Its inclusion conflicts with the script’s Windows focus on browser paths for Chrome, Edge, and Firefox. Errors on Windows—lacking /proc-are suppressed due to the try/except block, allowing execution to continue. This suggests the obfuscation framework is cross-platform. While ShadowCoil and ViperTunnel payloads target Windows, Linux anti-debugging hints at future Linux malware. The modular design allows code reuse across OSes, even if the payload is OS-specific.

Clustering#

Significant changes in code logic and obfuscation require a clear timeline of the backdoor’s evolution. Establishing a reliable timeline depends on correlating data points, mainly hardcoded C2 infrastructure and external metadata. C2 communication shows active deployment periods, but VirusTotal upload timestamps and domain registration records help fill gaps. We first present significant changes in the use of obfuscation and code logic to highlight development efforts and then use these clusters to chart chronological development. Clustering analysis found four variants of the final payload. Though they all establish a tunnel, differences in structure and implementation indicate iterative development. All samples used the same hardcoded credentials: AnyUser / AnyPassword. Based on the following clustering, the estimated timeframes for each group are as follows

GroupTimeframeEvidence Source
Early DevelopmentDecember 2023VirusTotal Upload Timestamps
Adopting of public ToolingSeptember 2024VirusTotal Upload Timestamps
Refinement & DebuggingDecember 2024 - September 2025VirusTotal Upload Timestamps + Domain Registrations
Modern Production VariantSeptember 2025 - December 2025VirusTotal Upload Timestamps + Domain Registrations + Archive Timestamps + Own Cases

An Overview of the clustering result, including chronological information, can be found in the following timeline. This timeline exclusively includes C2 servers that have been explicitly hardcoded within the observed samples.

Early Development#

The early development samples are dated to roughly the end of 2023. The samples in this group exhibit numerous coding inconsistencies and spelling errors. These samples lack a hardcoded command-and-control (C2) address and contain notable typos, such as deamon instead of daemon.

Additional errors include misspelled magic methods, such as __setatrr__ instead of __setattr__.

Further spelling errors are present, including verifing instead of verifying.

Some errors are in unused variables, indicating limited review or fragmented development. Other parts use standard exception handling for debugging, with some blocks including unused exception variables. These oversights suggest the malware is in an early or less mature stage of development. Additionally, multiple variables are declared but never used. Examples include:

The code shows inconsistent exception handling, with custom exceptions inheriting directly from Python’s built-in Exception class.

In other sections, a debug log is generated whenever an exception occurs.

Samples within this group were solely observed without any form of obfuscation. Furthermore, it is presumed that the samples submitted to VirusTotal in 2025 also originated in 2023.

Adoption of public Tooling#

The second cluster has a minified payload, removing whitespace and flattening multi-line logic into single lines. Identifiers are single-character, sometimes underscore-prefixed. Although dense, the lack of cryptographic obfuscation makes the logic easily recoverable, both manually and automatically.

Observed characteristics suggest this variant was for development or testing, indicated by verbose logging and an unused traceback import. Like the previous cluster, it lacks a hardcoded C2 address; the target IP is provided at runtime via argv. This minified form was observed only with the PyOBFUSCATE loader, distinguishing it from more mature, self-contained versions.

Refinement & Debugging#

One-line expressions are expanded into multi-line blocks, with whitespace added for clarity. Obfuscation remnants, like underscore-prefixed, single-character variables (e.g., _X), remain.

This group appears to be a development or debugging build, evidenced by a debug flag set with argparse, multiple logging levels, print statements, and an imported but unused traceback module. This cluster sits between heavily minified variants and refined production builds: it lacks advanced obfuscation but avoids the aggressive variable randomness seen in previous versions. Samples in this group were obfuscated using PyOBFUSCATE and the version described by eSentire, which represents an earlier version of the obfuscation described in this blog. Finally, this cluster introduced the usage of hardcoded C2 addresses.

Modern Production Variant#

The fourth group is distinguished by a notable architectural shift, with the codebase refactored into more clearly named classes: Wire, Relay, and Commander. This iteration exhibits the highest code quality among the observed samples, featuring a streamlined and professional implementation.

This cluster removes previous logging and argparse. Unlike earlier versions that supported configurable ports via the command line, the port is now hardcoded. Exception handling is consolidated: custom exceptions like ConnectionClosedError and ExitException are caught in simplified blocks with break or pass for quiet thread termination. These changes mark a shift from testing to a stealth-focused production payload. Samples in this group were observed using the obfuscation described in this blog and its previous version.

Hunting infrastructure#

With the method established, we extend the investigation to adversary infrastructure, uncovering relationships and insights. We pivot from a known C2 address to identify related infrastructure and data points.

The view shows open ports on a recent C2 server. Port 443, used for VIPERTUNNEL to C2, is known. Probing it returns only the static response 00 00.

High-numbered (five-digit) ports are probably relay ports. After connecting, the C2 directs VIPERTUNNEL to relay traffic to a secondary high port.

Port 22 is running SSH. There is little of interest here, as host keys vary by system. While the OpenSSH version is largely consistent across servers, it is not identical in all cases and therefore is not a reliable indicator.

This leaves port 8000, which returns an HTTP 401 response in Shodan.

This HTTP response is part of the Pyramid framework, a “C2” tool that evades EDR detection by using the LOLBin python.exe to run Python code in memory.

The framework delivers encrypted code using ChaCha20 or XOR and includes modules such as secretsdump.py and LaZagne.py for post-exploitation. Pyramid is likely used for post-exploitation after gaining access via VIPERTUNNEL and conducting reconnaissance. This linkage between VIPERTUNNEL and Pyramid has been previously documented by other researchers, including Intrinsec and hunt.io.

Confirming Pyramid’s presence in our incident was difficult due to its age, as it leaves minimal artifacts, mainly in memory and dropping few files. Yet for hunting, Pyramid source code shows a unique data pattern with HTTP 401 responses, aiding fingerprinting in investigations. Hunting only for Pyramid activity might reveal unrelated systems to VIPERTUNNEL, but a distinct identifier exists. Public source code shows default WWW-Authenticate as Basic realm="Demo Realm"; in our case, it’s set to Proxy. Below is the function responsible for the 401 Response in the Pyramid source code.

This deviation makes the deployment uniquely identifiable among other Pyramid installations, as these headers are returned with an HTTP 401 (Unauthorized) response.

Interestingly, the response has two Python Server headers based on BaseHTTP, but with different versions and runtimes. The cause is unclear, but it offers a pivot point. These traits help craft queries to find more C2 infrastructure.

Using this knowledge, we can craft preliminary Censys hunts to identify further C2’s such as

host.services.endpoints: (http.status_code: 401 and http.headers: (key: "Server" and value: "BaseHTTP") and http.headers: (key: "WWW-Authenticate" and value: "Basic realm=\"Proxy\"")) 

The hunts reveal that some systems do not use port 8000 for Pyramid. This is crucial because Pyramid hunts can’t rely solely on port 8000 to identify them.

In some matches, only ports 22 and 443 are open, according to historical data. Our attribution to VIPERTUNNEL relies on a modified HTTP 401 response, specifically, a Basic realm="Proxy" header, within Pyramid. This raises the question: how do we identify C2 servers not using Pyramid? One option is to use Censys to identify systems that return identical responses on port 443.

These results exceed those of previous hunts, with some new geolocations. Manual checks suggest some may be false positives.

Restricting results to SSH service, some matches remain low-confidence matches due to limited data. However, this approach also reveals previously missed systems, highlighting the need for multiple indicators and cross-methods analysis.

Refining this approach, we achieved the most robust results with the following Censys query. While some false positives remain, most matches are true-positives.

host.services.banner_hex = "0000" AND host.services.protocol: "SSH" AND "python" AND "basehttp"

Lastly, another method we can employ is using the HTTP Response Body as an indicator. The 401 HTTP Response also contains the body {"success": false, "error": "Auth required"} with the Body Hash 537a7628eb1b2fe117c9081e65579397a4d9b63f227802cac4028f4c34e2b337. Using this hash, we can also utilise other services, such as VirusTotal, to identify all systems returning this body hash. This approach can produce false positives, as other frameworks might use the same HTTP Response.

All found IOCs can be found in the appendix.

Analysing the Infrastructure#

To understand the infrastructure, we aggregated all publicly available network IOCs from the references and correlated them with prior hunting results. The dataset was enriched with metadata, including activity periods, ASN/ISP information, exposed ports, and HTTP response characteristics. Each IOC was reanalysed with a confidence score; conflicting entries were excluded from the final analysis. The adversary’s infrastructure shows a consistent technical stack. A key signature is open ports 22 (SSH) and 443 (HTTPS) across all nodes. Pyramid C2 particularly favours port 8000. There is a minor deviation in SSH configurations across hosting providers. On BlueVPS OU and HZ Hosting Ltd, the SSH service was moved to port 56777, likely reflecting their default settings rather than a tactical change. Fingerprinting the SSH service showed high homogeneity, with nearly 75% of C2 nodes returning a consistent banner: SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13. This identifies the OS as Ubuntu 22.04 LTS. While the campaign uses various ASNs and providers—showing no clear ISP trend—the geolocation is uniform: 100% of IPs are in the United States. Despite the campaign’s age, the infrastructure remains resilient. Most high-confidence C2s are still active, though few nodes are offline. Three servers stopped running the Pyramid listener, indicating decommissioning or tactic shifts. An ongoing mystery is Pyramid’s 401 responses, which often return double-headers, a behaviour under investigation for potential attribution. SSH host keys differ across systems, indicating the infrastructure isn’t based on shared VM templates. Recent C2 infrastructure from September to November 2025 shows only one Pyramid instance on port 1976 in the current Censys dataset. The reported Python and BaseHTTP versions appear inconsistent and likely randomized, as repeated curl requests from multiple IP addresses returned different headers, suggesting header spoofing to evade detection.

Most indicators were first observed or reported in January 2025, likely active earlier. A second wave of C2 infrastructure appeared around Q3 2025, showing expansion or refresh.

Conclusion#

Our analysis shows VIPERTUNNEL is transitioning. Low detection rates in recent samples indicate that it remains difficult to detect, suggesting effective obfuscation and code refinement.

The technical difference in obfuscation styles between ShadowCoil and ViperTunnel indicates separate authors or teams. The recent Linux debugger checks in ShadowCoil suggest a shift toward cross-platform capabilities. The infrastructure is maturing toward stealth by abandoning fixed ports like 8000 and using spoofed headers to hinder protocol detection. Transitioning to a custom 401 Response may evade generic scanners but offers a unique fingerprint for targeted hunting. Ultimately, the campaign’s status remains unclear. The credentials in the binaries appear to be placeholders, and the absence of new samples in 2026 suggests a lull or a shift in security posture. While these tools were deployed before the DragonForce ransomware incident, the connection remains under investigation and has not been confirmed.

Further References#

Indicators of Compromise#

Below are the references for the indicators in this article.

Command & Controller Servers#

Below is a list of all identified C2 Servers. This list only includes high-confidence systems and may therefore not be complete. The dates for “First seen” are based on various sources and should be regarded as estimates.

C2ASNISPFirst SeenLast SeenSource of “First Seen” Timestamp
38.146.28.93AS174Cogent CommunicationsJanuary 2025Still Activethreatfox
38.135.54.24AS26383Baxet Group Inc.21.09.2025Still Activewhois
162.248.225.165AS14576Hosting Solution Ltd.November 2025Still ActiveVirusTotal
CarryingItAll.com (193.5.65.151)AS395839HOSTKEY22.09.202511.12.2025VirusTotal
CarryingItAll.com (108.181.115.254)AS40676Psychz Networks11.12.2025Still ActiveVirusTotal
rentiantech.com (45.56.162.61)AS199959GWY IT PTY LTD10.07.2025Still Activewhois
185.174.101.69AS36352HostPapaJanuary 2025Still Activeguidepointsecurity / VirusTotal
104.238.61.144AS199959GWY IT PTY LTDJanuary 2025Still Activeguidepointsecurity
88.119.175.65AS61272Informacines sistemos ir technologijos, UABJanuary 2025Still Activeguidepointsecurity
92.118.112.208AS215540GLOBAL CONNECTIVITY SOLUTIONS LLPJanuary 2024Still Activereliaquest
173.44.141.226AS62904Eonix CorporationJanuary 2025Still Activeguidepointsecurity
185.174.101.240AS36352HostPapaJanuary 2025Still Activeguidepointsecurity
108.181.115.171AS40676Psychz NetworksJanuary 2025Still Activeguidepointsecurity
162.252.173.12AS9009M247 Europe SRLJanuary 2025Still Activethreatfox
108.181.182.143AS40676Psychz NetworksJanuary 2025Still Activeguidepointsecurity
23.227.193.172AS29802HIVELOCITY, Inc.January 2025December 2025guidepointsecurity
88.119.175.70AS61272Informacines sistemos ir technologijos, UABJanuary 2025Still Activeguidepointsecurity
193.203.49.90AS204957GREEN FLOID LLCJanuary 2025Still Activethreatfox
185.33.86.15AS202015HZ Hosting LtdJanuary 2025Still Activethreatfox
37.1.212.18AS29802HIVELOCITY, Inc.January 2025March 2026guidepointsecurity
38.180.81.153AS29802HIVELOCITY, Inc.January 2025Still Activeguidepointsecurity
45.66.248.150AS62005BlueVPS OUJanuary 2025February 2026guidepointsecurity
158.255.213.22AS9009M247 Europe SRLJanuary 2025Still Activethreatfox
162.248.224.223 (chateaugalicia.com)AS14576Hosting Solution Ltd.August 2025Still Activethreatfox
104.238.60.108AS199959GWY IT PTY LTDJanuary 2026Still Activethreatfox
185.233.166.124AS398256AS-ULTRAHOSTAugust 2025Still Activethreatfox
185.72.8.65AS26383Baxet Group Inc.July 2025Still Activethreatfox
92.118.112.143AS215540GLOBAL CONNECTIVITY SOLUTIONS LLPJanuary 202529.06.2025threatfox
45.82.85.50AS36352HostPapaJanuary 2025Still Activethreatfox
185.72.8.121AS26383Baxet Group Inc.February 2026Still Activethreatfox
joealdana.com (185.180.198.3)AS14576Hosting Solution Ltd.19.09.2025Still Activewhois
185.72.8.137AS26383Baxet Group Inc.October 2025Still Activethreatfox

Yara Rules#

The following Yara Rules have been used for hunting on VirusTotal; therefore, some depend on VirusTotal’s exclusive modules.

rule AV_match_shcoil {
    meta:
        author = "Evgen Blohm @ InfoGuard AG"
        description = "Hunt for samples that have ShadowCoil related AV matches that often also used for VIPERTUNNEL samples"
        date = "2025-02-09"
    condition:
        for any engine, signature in vt.metadata.signatures : (
            signature contains "ShCoil" or signature contains "ShadowCoil"
        )
}

rule ShadowCoil_Packed_Python
{
    meta:
        author = "YungBinary"
        description = "Modified the original rule "
        target_entity = "file"
        source = "https://www.esentire.com/blog/unpacking-shadowcoils-ransomhub-ex-affiliate-credential-harvesting-tool"
    strings:
        $a = "exec(pc_start(" ascii
        $b = "get_hw_key():" ascii
        $c = "'vm', 'virtual'" ascii
        $d = "TracerPid:" ascii
    condition:
        vt.metadata.file_type == vt.FileType.PYTHON and filesize < 60KB and all of them
}

rule MALWARE_vipertunnel {
    meta:
        author = "Evgen Blohm"
        description = "hunting rule for socks5 backdoor aka VIPERTUNNEL"

    strings:
        // Unique Class Names and Functions
        $class1 = "class Wire(" ascii wide
        $class2 = "class Relay(" ascii wide
        $class3 = "class Commander(" ascii wide
        $class4 = "class ControllerCommandConnection" ascii wide
        $class5 = "class MySocket" ascii wide
        $class6 = "class MyServiceSocket" ascii wide
        $class7 = "class Handler" ascii wide

        // Custom Exception Names
        $exc1 = "ConnectionTimeoutOccuredError" ascii wide
        $exc2 = "BadLengthData" ascii wide
        $exc3 = "CloseException" ascii wide
        $exc4 = "ExitException" ascii wide
        $exc5 = "ConnectionClosedError" ascii wide
        $exc6 = "ConnectionResetError" ascii wide
        $exc7 = "ConnectionAbortedError" ascii wide
        $exc8 = "ConnectionRefusedError" ascii wide

        // Logic-specific strings
        $logic1 = "self.must_equal = " ascii wide
        $logic2 = "struct.unpack('BBB', self." ascii wide
        $logic3 = "def _bridge(self):" ascii wide
        $logic4 = "socket.socket = Wire" ascii wide

        // unique variables
        $var1 = "main_flag_to_close" ascii wide
        $var2 = "TIMEOUT_FOR_SOCKET_OPERATION" ascii wide

        // Unique Identifiers
        $user = "AnyLogin" ascii wide
        $secret = "AnyPassword" ascii wide

    condition:
        filesize < 60KB and vt.metadata.file_type == vt.FileType.PYTHON and (
            (3 of ($class*) and 3 of ($exc*)) or
            (2 of ($class*) and 2 of ($exc*) and 1 of ($logic*) and 1 of ($var*)) or 
            ($user and $secret)
        )
}

rule MALWARE_vipertunnel_typos{
    meta:
        description = "Hunting for ViperTunnel Python Proxies using some left over typos"
    strings:
        $typo1 = "__setatrr__" ascii wide // __setattr__
        $typo2 = "deamon" ascii wide // daemon
        $typo3 = "allow_no_verifing" ascii wide // allow_no_verifying
        $typo4 = "ConnectionTimeoutOccuredError" ascii wide // ConnectionTimeoutOccurredError
        $typo5 = "% ((target" ascii wide // logged as tuple

    condition:
        filesize < 60KB and vt.metadata.file_type == vt.FileType.PYTHON and (
            (3 of ($typo*))
        )
}

rule MALWARE_vipertunnel_obfuscation {
    meta:
        author = "Evgen Blohm"
        description = "Finding files using the same obfuscation as in IR1361"

    strings:
        // decoding/decryption related keywords
        $crypto1 = "b85decode" ascii wide
        $crypto2 = "PBKDF2" ascii wide
        $crypto3 = "HKDF" ascii wide
        $crypto4 = "MODE_GCM" ascii wide
        $crypto5 = "MODE_CTR" ascii wide
        $crypto6 = "ChaCha20" ascii wide
        $crypto7 = "blake3" ascii wide

        // dynamically imported functions
        $imp1 = "join" ascii wide
        $imp2 = "chr" ascii wide
        $imp3 = "map" ascii wide
        $imp4 = "map" ascii wide
        $imp5 = "zip" ascii wide
        $imp6 = "bytes" ascii wide
        $imp7 = "getattr" ascii wide
        $imp8 = "builtins" ascii wide

        // static imports
        $s_imp1 = "import zlib" ascii wide
        $s_imp2 = "import sys" ascii wide
        $s_imp3 = "import base64" ascii wide
        $s_imp4 = "import AES" ascii wide
        $s_imp5 = "import SHA256" ascii wide
        $s_imp6 = "import os" ascii wide
        $s_imp7 = "import blake3" ascii wide
        $s_imp8 = "import hashlib" ascii wide

        // base85 encoded chars/strings
        $enc4 = "WNBw*b94" ascii wide // digest
        $enc5 = "WMyM=d2n<" ascii wide // decrypt
        $enc6 = "V{dhCbN" ascii wide // count
        $enc7 = "Zf|a5Wd" ascii wide // nonce
        $enc8 = "WMyM=d2n=JVQyq!c4cyDW_b" ascii wide // decrypt_and_verify
        $enc9 = "Wq4&{" ascii wide // exec

    condition:
        vt.metadata.file_type == vt.FileType.PYTHON and filesize < 40KB and (
            (3 of ($crypto*) and 3 of ($imp*) and 2 of ($s_imp*) and 2 of ($enc*))
        )
}

rule MALWARE_vipertunnel_obfuscation_old {
    meta:
        author = "Evgen Blohm"
        description = ""
    strings:
        $func1 = "decode('utf-8')" ascii wide
        $func2 = "int.from_bytes" ascii wide
        $func3 = "while True:" ascii wide
        $func4 = "sys.exit(0)" ascii wide

        $imp1 = "import blake3" ascii wide
        $imp2 = "import base64" ascii wide
        $imp3 = "import hashlib" ascii wide

    condition:
        filesize > 10KB and filesize < 80KB and vt.metadata.file_type == vt.FileType.PYTHON and (
            (3 of ($func*) and 3 of ($imp*))
        )
}

Hashes#

All Files were uploaded to VirusTotal and saved in a Collection

Slithering Through the Noise - Deep Dive into the VIPERTUNNEL Python Backdoor
https://labs.infoguard.ch/posts/slithering_through_the_noise/
Author
Evgen Blohm
Published at
2026-04-09